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/QPainter>
59 #include <QtGui/QBitmap>
60 #include <QtGui/QMovie>
61 #include <QtGui/QWidget>
62 #include <QtCore/QDebug>
63 #include <kauthorized.h>
65 #include <kio/jobuidelegate.h>
66 #include <kio/jobclasses.h>
68 #include <kcharsets.h>
69 #include <kiconloader.h>
70 #include <scheduler.h>
73 #include <khtml_global.h>
74 #include <khtml_part.h>
78 #include <kfilemetainfo.h>
79 #include <ktemporaryfile.h>
82 #include "html/html_documentimpl.h"
83 #include "css/css_stylesheetimpl.h"
84 #include "xml/dom_docimpl.h"
86 #include "blocked_icon.cpp"
88 using namespace khtml
;
90 using namespace khtmlImLoad
;
92 #define MAX_LRU_LISTS 20
97 LRUList() : m_head(0), m_tail(0) {}
100 static LRUList m_LRULists
[MAX_LRU_LISTS
];
101 static LRUList
* getLRUListFor(CachedObject
* o
);
103 CachedObjectClient::~CachedObjectClient()
107 CachedObject::~CachedObject()
109 Cache::removeFromLRUList(this);
112 void CachedObject::finish()
117 bool CachedObject::isExpired() const
119 if (!m_expireDate
) return false;
120 time_t now
= time(0);
121 return (difftime(now
, m_expireDate
) >= 0);
124 void CachedObject::setRequest(Request
*_request
)
126 if ( _request
&& !m_request
)
129 if ( allowInLRUList() )
130 Cache::removeFromLRUList( this );
132 m_request
= _request
;
134 if ( allowInLRUList() )
135 Cache::insertInLRUList( this );
138 void CachedObject::ref(CachedObjectClient
*c
)
140 if (m_preloadResult
== PreloadNotReferenced
) {
142 m_preloadResult
= PreloadReferencedWhileComplete
;
143 else if (m_prospectiveRequest
)
144 m_preloadResult
= PreloadReferencedWhileLoading
;
146 m_preloadResult
= PreloadReferenced
;
148 // unfortunately we can be ref'ed multiple times from the
149 // same object, because it uses e.g. the same foreground
150 // and the same background picture. so deal with it.
151 // Hence the use of a QHash rather than of a QSet.
152 m_clients
.insertMulti(c
,c
);
153 Cache::removeFromLRUList(this);
157 void CachedObject::deref(CachedObjectClient
*c
)
160 assert( m_clients
.count() );
161 assert( !canDelete() );
162 assert( m_clients
.contains( c
) );
168 if (allowInLRUList())
169 Cache::insertInLRUList(this);
172 void CachedObject::setSize(int size
)
176 if ( !m_next
&& !m_prev
&& getLRUListFor(this)->m_head
!= this )
179 sizeChanged
= ( size
- m_size
) != 0;
181 // The object must now be moved to a different queue,
182 // since its size has been changed.
183 if ( sizeChanged
&& allowInLRUList())
184 Cache::removeFromLRUList(this);
188 if ( sizeChanged
&& allowInLRUList())
189 Cache::insertInLRUList(this);
192 QTextCodec
* CachedObject::codecForBuffer( const QString
& charset
, const QByteArray
& buffer
) const
194 // we don't use heuristicContentMatch here since it is a) far too slow and
195 // b) having too much functionality for our case.
197 uchar
* d
= ( uchar
* ) buffer
.data();
198 int s
= buffer
.size();
202 d
[0] == 0xef && d
[1] == 0xbb && d
[2] == 0xbf)
203 return QTextCodec::codecForMib( 106 ); // UTF-8
205 if ( s
>= 2 && ((d
[0] == 0xff && d
[1] == 0xfe) ||
206 (d
[0] == 0xfe && d
[1] == 0xff)))
207 return QTextCodec::codecForMib( 1000 ); // UCS-2
210 if(!charset
.isEmpty())
212 QTextCodec
* c
= KGlobal::charsets()->codecForName(charset
);
213 if(c
->mibEnum() == 11) {
214 // iso8859-8 (visually ordered)
215 c
= QTextCodec::codecForName("iso8859-8-i");
221 return QTextCodec::codecForMib( 4 ); // latin 1
224 // -------------------------------------------------------------------------------------------
226 CachedCSSStyleSheet::CachedCSSStyleSheet(DocLoader
* dl
, const DOMString
&url
, KIO::CacheControl _cachePolicy
,
228 : CachedObject(url
, CSSStyleSheet
, _cachePolicy
, 0)
230 // Set the type we want (probably css or xml)
231 QString ah
= QLatin1String( accept
);
237 m_wasBlocked
= false;
240 Cache::loader()->load(dl
, this, false, true /*highPriority*/);
244 CachedCSSStyleSheet::CachedCSSStyleSheet(const DOMString
&url
, const QString
&stylesheet_data
)
245 : CachedObject(url
, CSSStyleSheet
, KIO::CC_Verify
, stylesheet_data
.length())
248 m_status
= Persistent
;
249 m_sheet
= DOMString(stylesheet_data
);
252 bool khtml::isAcceptableCSSMimetype( const DOM::DOMString
& mimetype
)
254 // matches Mozilla's check (cf. nsCSSLoader.cpp)
255 return mimetype
.isEmpty() || mimetype
== "text/css" || mimetype
== "application/x-unknown-content-type";
258 void CachedCSSStyleSheet::ref(CachedObjectClient
*c
)
260 CachedObject::ref(c
);
264 c
->error( m_err
, m_errText
);
266 c
->setStyleSheet( m_url
, m_sheet
, m_charset
, m_mimetype
);
270 void CachedCSSStyleSheet::data( QBuffer
&buffer
, bool eof
)
274 setSize(buffer
.buffer().size());
276 // QString charset = checkCharset( buffer.buffer() );
278 if (!m_charset
.isEmpty()) {
279 c
= KGlobal::charsets()->codecForName(m_charset
);
280 if(c
->mibEnum() == 11) c
= QTextCodec::codecForName("iso8859-8-i");
283 c
= codecForBuffer( m_charsetHint
, buffer
.buffer() );
284 m_charset
= c
->name();
286 QString data
= c
->toUnicode( buffer
.buffer().data(), m_size
);
287 // workaround Qt bugs
288 m_sheet
= static_cast<QChar
>(data
[0]) == QChar::ByteOrderMark
? DOMString(data
.mid( 1 ) ) : DOMString(data
);
294 void CachedCSSStyleSheet::checkNotify()
296 if(m_loading
|| m_hadError
) return;
298 CDEBUG
<< "CachedCSSStyleSheet:: finishedLoading " << m_url
.string() << endl
;
300 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
301 it
.next().value()->setStyleSheet( m_url
, m_sheet
, m_charset
, m_mimetype
);
305 void CachedCSSStyleSheet::error( int err
, const char* text
)
312 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
313 it
.next().value()->error( m_err
, m_errText
);
317 QString
CachedCSSStyleSheet::checkCharset(const QByteArray
& buffer
) const
319 int s
= buffer
.size();
320 if (s
<= 12) return m_charset
;
322 // @charset has to be first or directly after BOM.
323 // CSS 2.1 says @charset should win over BOM, but since more browsers support BOM
324 // than @charset, we default to that.
325 const char* d
= (const char*) buffer
.data();
326 if (strncmp(d
, "@charset \"",10) == 0)
328 // the string until "; is the charset name
329 char *p
= strchr(d
+10, '"');
330 if (p
== 0) return m_charset
;
331 QString charset
= QString::fromAscii(d
+10, p
-(d
+10));
338 // -------------------------------------------------------------------------------------------
340 CachedScript::CachedScript(DocLoader
* dl
, const DOMString
&url
, KIO::CacheControl _cachePolicy
, const char*)
341 : CachedObject(url
, Script
, _cachePolicy
, 0)
343 // It's javascript we want.
344 // But some websites think their scripts are <some wrong mimetype here>
345 // and refuse to serve them if we only accept application/x-javascript.
346 setAccept( QLatin1String("*/*") );
348 Cache::loader()->load(dl
, this, false);
353 CachedScript::CachedScript(const DOMString
&url
, const QString
&script_data
)
354 : CachedObject(url
, Script
, KIO::CC_Verify
, script_data
.length())
358 m_status
= Persistent
;
359 m_script
= DOMString(script_data
);
362 void CachedScript::ref(CachedObjectClient
*c
)
364 CachedObject::ref(c
);
366 if(!m_loading
) c
->notifyFinished(this);
369 void CachedScript::data( QBuffer
&buffer
, bool eof
)
373 setSize(buffer
.buffer().size());
375 QTextCodec
* c
= codecForBuffer( m_charset
, buffer
.buffer() );
376 QString data
= c
->toUnicode( buffer
.buffer().data(), m_size
);
377 m_script
= static_cast<QChar
>(data
[0]) == QChar::ByteOrderMark
? DOMString(data
.mid( 1 ) ) : DOMString(data
);
382 void CachedScript::checkNotify()
384 if(m_loading
) return;
386 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
387 it
.next().value()->notifyFinished(this);
390 void CachedScript::error( int /*err*/, const char* /*text*/ )
397 // ------------------------------------------------------------------------------------------
399 static QString
buildAcceptHeader()
401 return "image/png, image/jpeg, video/x-mng, image/jp2, image/gif;q=0.5,*/*;q=0.1";
404 // -------------------------------------------------------------------------------------
406 CachedImage::CachedImage(DocLoader
* dl
, const DOMString
&url
, KIO::CacheControl _cachePolicy
, const char*)
407 : QObject(), CachedObject(url
, Image
, _cachePolicy
, 0)
409 static const QString
&acceptHeader
= KGlobal::staticQString( buildAcceptHeader() );
411 i
= new khtmlImLoad::Image(this);
416 bgColor
= qRgba( 0, 0, 0, 0 );
418 isFullyTransparent
= false;
423 setAccept( acceptHeader
);
424 i
->setShowAnimations(dl
->showAnimations());
427 if ( KHTMLGlobal::defaultHTMLSettings()->isAdFiltered( url
.string() ) ) {
429 CachedObject::finish();
433 CachedImage::~CachedImage()
439 void CachedImage::ref( CachedObjectClient
*c
)
441 CachedObject::ref(c
);
444 kDebug(6060) << " image "<<this<<" ref'd by client " << c
<< "\n";
447 // for mouseovers, dynamic changes
448 //### having both makes no sense
449 if ( m_status
>= Persistent
&& !pixmap_size().isNull() ) {
451 kDebug(6060) << "Notifying finished size:" <<
452 i
->size().width() << ", " << i
->size().height() << endl
;
454 c
->updatePixmap( QRect(QPoint(0, 0), pixmap_size()),
456 c
->notifyFinished( this );
460 void CachedImage::deref( CachedObjectClient
*c
)
462 CachedObject::deref(c
);
463 /* if(m && m_clients.isEmpty() && m->running())
467 #define BGMINWIDTH 32
468 #define BGMINHEIGHT 32
470 QPixmap
CachedImage::tiled_pixmap(const QColor
& newc
, int xWidth
, int xHeight
)
473 // no error indication for background images
474 if(m_hadError
||m_wasBlocked
) return *Cache::nullPixmap
;
476 // If we don't have size yet, nothing to draw yet
477 if (i
->size().width() == 0 || i
->size().height() == 0)
478 return *Cache::nullPixmap
;
481 #warning "Needs some additional performance work"
484 static QRgb bgTransparent
= qRgba( 0, 0, 0, 0 );
486 QSize
s(pixmap_size());
490 if (w
== -1) xWidth
= w
= s
.width();
491 if (h
== -1) xHeight
= h
= s
.height();
493 if ( ( (bgColor
!= bgTransparent
) && (bgColor
!= newc
.rgba()) ) ||
494 ( bgSize
!= QSize(xWidth
, xHeight
)) )
502 const QPixmap
* src
; //source for pretiling, if any
504 const QPixmap
&r
= pixmap(); //this is expensive
505 if (r
.isNull()) return r
;
507 //See whether we should scale
508 if (xWidth
!= s
.width() || xHeight
!= s
.height()) {
509 src
= scaled_pixmap(xWidth
, xHeight
);
514 bgSize
= QSize(xWidth
, xHeight
);
516 //See whether we can - and should - pre-blend
517 // ### this needs serious investigations. Not likely to help with transparent bgColor,
518 // won't work with CSS3 multiple backgrounds. Does it help at all in Qt4? (ref: #114938)
519 if (newc
.isValid() && (r
.hasAlpha() || r
.hasAlphaChannel())) {
520 bg
= new QPixmap(xWidth
, xHeight
);
523 p
.drawPixmap(0, 0, *src
);
524 bgColor
= newc
.rgba();
527 bgColor
= bgTransparent
;
530 //See whether to pre-tile.
533 if ( r
.width() < BGMINWIDTH
)
534 w
= ((BGMINWIDTH
-1) / xWidth
+ 1) * xWidth
;
535 if ( r
.height() < BGMINHEIGHT
)
536 h
= ((BGMINHEIGHT
-1) / xHeight
+ 1) * xHeight
;
539 if ( w
!= xWidth
|| h
!= xHeight
)
541 // kDebug() << "pre-tiling " << s.width() << "," << s.height() << " to " << w << "," << h;
543 bg
= new QPixmap(w
, h
);
544 if (src
->hasAlpha() || src
->hasAlphaChannel()) {
545 if (newc
.isValid() && (bgColor
!= bgTransparent
))
548 bg
->fill( Qt::transparent
);
552 p
.drawTiledPixmap(0, 0, w
, h
, *src
);
557 } else if (src
&& !bg
) {
558 // we were asked for the entire pixmap. Cache it.
559 // ### goes against imload stuff, but it's far too expensive
560 // to recreate the full pixmap each time it's requested as
561 // we don't know what portion of it will be used eventually
562 // (by e.g. paintBackgroundExtended). It could be a few pixels of
563 // a huge image. See #140248/#1 for an obvious example.
564 // Imload probably needs to handle all painting in paintBackgroundExtended.
565 bg
= new QPixmap(*src
);
575 QPixmap
* CachedImage::scaled_pixmap( int xWidth
, int xHeight
)
577 // no error indication for background images
578 if(m_hadError
||m_wasBlocked
) return Cache::nullPixmap
;
580 // If we don't have size yet, nothing to draw yet
581 if (i
->size().width() == 0 || i
->size().height() == 0)
582 return Cache::nullPixmap
;
585 if (scaled
->width() == xWidth
&& scaled
->height() == xHeight
)
590 //### this is quite awful performance-wise. It should avoid
591 // alpha if not needed, and go to pixmap, etc.
592 QImage
im(xWidth
, xHeight
, QImage::Format_ARGB32_Premultiplied
);
595 paint
.setCompositionMode(QPainter::CompositionMode_Source
);
596 ImagePainter
pi(i
, QSize(xWidth
, xHeight
));
597 pi
.paint(0, 0, &paint
);
600 scaled
= new QPixmap(QPixmap::fromImage(im
));
605 QPixmap
CachedImage::pixmap( ) const
608 return *Cache::brokenPixmap
;
611 return *Cache::blockedPixmap
;
613 int w
= i
->size().width();
614 int h
= i
->size().height();
617 QImage
im(w
, h
, QImage::Format_ARGB32_Premultiplied
);
620 paint
.setCompositionMode(QPainter::CompositionMode_Source
);
622 pi
.paint(0, 0, &paint
);
624 return QPixmap::fromImage( im
);
629 pi
.paint(0, 0, &paint
);
636 QSize
CachedImage::pixmap_size() const
638 if (m_wasBlocked
) return Cache::blockedPixmap
->size();
639 if (m_hadError
) return Cache::brokenPixmap
->size();
640 if (i
) return i
->size();
645 void CachedImage::imageHasGeometry(khtmlImLoad::Image
* /*img*/, int width
, int height
)
648 kDebug(6060) << this << " got geometry "<< width
<< "x" << height
;
650 do_notify(QRect(0, 0, width
, height
));
653 void CachedImage::imageChange (khtmlImLoad::Image
* /*img*/, QRect region
)
656 kDebug(6060) << "Image " << this << " change " <<
657 region
.x() << "," << region
.y() << ":" << region
.width() << "x" << region
.height() << endl
;
659 //### this is overly conservative -- I guess we need to also specify reason,
660 //e.g. repaint vs. changed !!!
667 void CachedImage::doNotifyFinished()
669 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
671 it
.next().value()->notifyFinished(this);
675 void CachedImage::imageError(khtmlImLoad::Image
* /*img*/)
681 void CachedImage::imageDone(khtmlImLoad::Image
* /*img*/)
684 kDebug(6060)<<"Image is done:" << this;
686 m_status
= Persistent
;
691 // QRect CachedImage::valid_rect() const
693 // if (m_wasBlocked) return Cache::blockedPixmap->rect();
694 // return (m_hadError ? Cache::brokenPixmap->rect() : m ? m->frameRect() : ( p ? p->rect() : QRect()) );
698 void CachedImage::do_notify(const QRect
& r
)
700 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
703 kDebug(6060) << " image "<<this<<" notify of geom client " << it
.peekNext() << "\n";
705 it
.next().value()->updatePixmap( r
, this);
710 // void CachedImage::movieUpdated( const QRect& r )
712 // #ifdef LOADER_DEBUG
713 // qDebug("movie updated %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
714 // m->framePixmap().size().width(), m->framePixmap().size().height());
717 // do_notify(m->framePixmap(), r);
721 void CachedImage::movieStatus(int status
)
723 #warning QMovie emits different signals now and requires different ways to call
725 qDebug("movieStatus(%d)", status
);
728 // ### the html image objects are supposed to send the load event after every frame (according to
729 // netscape). We have a problem though where an image is present, and js code creates a new Image object,
730 // which uses the same CachedImage, the one in the document is not supposed to be notified
732 // just another Qt 2.2.0 bug. we cannot call
733 // QMovie::frameImage if we're after QMovie::EndOfMovie
734 if(status
== QMovie::EndOfFrame
)
736 const QImage
& im
= m
->frameImage();
737 monochrome
= ( ( im
.depth() <= 8 ) && ( im
.numColors() - int( im
.hasAlphaBuffer() ) <= 2 ) );
738 for (int i
= 0; monochrome
&& i
< im
.numColors(); ++i
)
739 if (im
.colorTable()[i
] != qRgb(0xff, 0xff, 0xff) &&
740 im
.colorTable()[i
] != qRgb(0x00, 0x00, 0x00))
742 if( (im
.width() < 5 || im
.height() < 5) && im
.hasAlphaBuffer()) // only evaluate for small images
744 QImage am
= im
.createAlphaMask();
748 for(int y
= 0; y
< am
.height(); y
++)
749 for(int x
= 0; x
< am
.width(); x
++)
750 if(am
.pixelIndex(x
, y
)) {
754 isFullyTransparent
= (!solid
);
758 // we have to delete our tiled bg variant here
759 // because the frame has changed (in order to keep it in sync)
764 if((status
== QMovie::EndOfMovie
&& (!m
|| m
->frameNumber() <= 1)) ||
765 ((status
== QMovie::EndOfLoop
) && (m_showAnimations
== KHTMLSettings::KAnimationLoopOnce
)) ||
766 ((status
== QMovie::EndOfFrame
) && (m_showAnimations
== KHTMLSettings::KAnimationDisabled
))
771 setShowAnimations( KHTMLSettings::KAnimationDisabled
);
773 // monochrome alphamasked images are usually about 10000 times
774 // faster to draw, so this is worth the hack
775 if (p
&& monochrome
&& p
->depth() > 1)
777 QPixmap
* pix
= new QPixmap
;
778 pix
->convertFromImage( p
->convertToImage().convertDepth( 1 ), Qt::MonoOnly
|Qt::AvoidDither
);
779 pix
->setMask( p
->mask() );
785 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
786 it
.next().value()->notifyFinished( this );
787 m_status
= Cached
; //all done
790 if((status
== QMovie::EndOfFrame
) || (status
== QMovie::EndOfMovie
))
793 QRect
r(valid_rect());
794 qDebug("movie Status frame update %d/%d/%d/%d, pixmap size %d/%d", r
.x(), r
.y(), r
.right(), r
.bottom(),
795 pixmap().size().width(), pixmap().size().height());
797 do_notify(pixmap(), valid_rect());
802 // void CachedImage::movieResize(const QSize& /*s*/)
804 // do_notify(m->framePixmap(), QRect());
807 void CachedImage::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations
)
810 i
->setShowAnimations(showAnimations
);
813 // void CachedImage::deleteMovie()
818 void CachedImage::pauseAnimations()
820 // if ( m ) m->setPaused( true );
823 void CachedImage::resumeAnimations()
825 // if ( m ) m->setPaused( false );
828 void CachedImage::clear()
830 delete i
; i
= new khtmlImLoad::Image(this);
831 delete scaled
; scaled
= 0;
832 bgColor
= qRgba( 0, 0, 0, 0xff );
834 bgSize
= QSize(-1,-1);
840 // No need to delete imageSource - QMovie does it for us
844 void CachedImage::data ( QBuffer
&_buffer
, bool eof
)
847 kDebug( 6060 ) << this << "in CachedImage::data(buffersize " << _buffer
.buffer().size() <<", eof=" << eof
<< " pos:" << _buffer
.pos();
849 i
->processData((uchar
*)_buffer
.data().data(), _buffer
.pos());
856 //if ( !typeChecked )
858 // don't attempt incremental loading if we have all the data already
862 #warning QImage* requires heavy porting
865 formatType
= QImageDecoder::formatName( (const uchar
*)_buffer
.buffer().data(), _buffer
.size());
866 if ( formatType
&& strcmp( formatType
, "PNG" ) == 0 )
867 formatType
= 0; // Some png files contain multiple images, we want to show only the first one
871 if ( formatType
) // movie format exists
873 // imgSource = new ImageSource( _buffer.buffer());
874 // m = new QMovie( imgSource, 8192 );
875 m
= new QMovie( _buffer
);
876 m
->connectUpdate( this, SLOT( movieUpdated( const QRect
&) ));
877 m
->connectStatus( this, SLOT( movieStatus(int)));
878 m
->connectResize( this, SLOT( movieResize( const QSize
& ) ) );
884 imgSource
->setEOF(eof
);
885 imgSource
->maybeReady();
890 // QMovie currently doesn't support all kinds of image formats
891 // so we need to use a QPixmap here when we finished loading the complete
892 // picture and display it then all at once.
893 if(typeChecked
&& !formatType
)
896 kDebug(6060) << "CachedImage::data(): reloading as pixmap:";
900 QBuffer
buffer(_buffer
.buffer());
901 buffer
.open(IO_ReadOnly
);
902 QImageIO
io( &buffer
, 0 );
903 io
.setGamma(2.2); // hardcoded "reasonable value"
904 bool result
= io
.read();
905 if (result
) p
->convertFromImage(io
.image(), 0);
908 // set size of image.
910 kDebug(6060) << "CachedImage::data(): image is null: " << p
->isNull();
915 do_notify(pixmap(), QRect(0, 0, 16, 16)); // load "broken image" icon
918 do_notify(*p
, p
->rect());
920 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
921 it
.next().value()->notifyFinished( this );
922 m_status
= Cached
; //all done
928 void CachedImage::finish()
930 //Status oldStatus = m_status;
931 CachedObject::finish();
932 /* if ( oldStatus != m_status ) {
933 const QPixmap &pm = pixmap();
934 do_notify( pm, pm.rect() );
937 QSize s
= pixmap_size();
938 setSize( s
.width() * s
.height() * 2);
942 void CachedImage::error( int /*err*/, const char* /*text*/ )
948 do_notify(QRect(0, 0, 16, 16));
949 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
950 it
.next().value()->notifyFinished(this);
953 // -------------------------------------------------------------------------------------------
955 CachedSound::CachedSound(DocLoader
* dl
, const DOMString
&url
, KIO::CacheControl _cachePolicy
, const char*)
956 : CachedObject(url
, Sound
, _cachePolicy
, 0)
958 setAccept( QLatin1String("*/*") ); // should be whatever phonon would accept...
959 Cache::loader()->load(dl
, this, false);
963 void CachedSound::ref(CachedObjectClient
*c
)
965 CachedObject::ref(c
);
967 if(!m_loading
) c
->notifyFinished(this);
970 void CachedSound::data( QBuffer
&buffer
, bool eof
)
974 setSize(buffer
.buffer().size());
976 m_sound
= buffer
.buffer();
981 void CachedSound::checkNotify()
983 if(m_loading
) return;
985 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
986 it
.next().value()->notifyFinished(this);
989 void CachedSound::error( int /*err*/, const char* /*text*/ )
995 // ------------------------------------------------------------------------------------------
997 Request::Request(DocLoader
* dl
, CachedObject
*_object
, bool _incremental
)
1000 object
->setRequest(this);
1001 incremental
= _incremental
;
1007 object
->setRequest(0);
1010 // ------------------------------------------------------------------------------------------
1012 DocLoader::DocLoader(KHTMLPart
* part
, DocumentImpl
* doc
)
1014 m_cachePolicy
= KIO::CC_Verify
;
1016 m_creationDate
= time(0);
1017 m_bautoloadImages
= true;
1018 m_showAnimations
= KHTMLSettings::KAnimationEnabled
;
1022 Cache::docloader
->append( this );
1025 DocLoader::~DocLoader()
1028 Cache::loader()->cancelRequests( this );
1029 Cache::docloader
->removeAll( this );
1032 void DocLoader::setCacheCreationDate(time_t _creationDate
)
1035 m_creationDate
= _creationDate
;
1037 m_creationDate
= time(0); // Now
1040 void DocLoader::setExpireDate(time_t _expireDate
, bool relative
)
1043 m_expireDate
= _expireDate
+ m_creationDate
; // Relative date
1045 m_expireDate
= _expireDate
; // Absolute date
1047 kDebug(6061) << "docLoader: " << m_expireDate
- time(0) << " seconds left until reload required.\n";
1051 void DocLoader::insertCachedObject( CachedObject
* o
) const
1053 m_docObjects
.insert( o
);
1056 bool DocLoader::needReload(CachedObject
*existing
, const QString
& fullURL
)
1058 bool reload
= false;
1059 if (m_cachePolicy
== KIO::CC_Verify
||
1060 // During a softReload, we favour using cached images
1061 // over forcibly re-downloading them.
1062 (existing
->type() == CachedObject::Image
&& m_part
->browserExtension()->browserArguments().softReload
))
1064 if (!m_reloadedURLs
.contains(fullURL
))
1066 if (existing
&& existing
->isExpired() && !existing
->isPreloaded())
1068 Cache::removeCacheEntry(existing
);
1069 m_reloadedURLs
.append(fullURL
);
1074 else if ((m_cachePolicy
== KIO::CC_Reload
) || (m_cachePolicy
== KIO::CC_Refresh
))
1076 if (!m_reloadedURLs
.contains(fullURL
))
1078 if (existing
&& !existing
->isPreloaded())
1080 Cache::removeCacheEntry(existing
);
1082 if (!existing
|| !existing
->isPreloaded()) {
1083 m_reloadedURLs
.append(fullURL
);
1091 void DocLoader::registerPreload(CachedObject
* resource
)
1093 if (!resource
|| resource
->isLoaded() || m_preloads
.contains(resource
))
1095 resource
->increasePreloadCount();
1096 m_preloads
.insert(resource
);
1097 resource
->setProspectiveRequest();
1098 #ifdef PRELOAD_DEBUG
1099 fprintf(stderr
, "PRELOADING %s\n", resource
->url().string().toLatin1().data());
1103 void DocLoader::clearPreloads()
1105 printPreloadStats();
1106 QSet
<CachedObject
*>::iterator end
= m_preloads
.end();
1107 for (QSet
<CachedObject
*>::iterator it
= m_preloads
.begin(); it
!= end
; ++it
) {
1108 CachedObject
* res
= *it
;
1109 res
->decreasePreloadCount();
1110 if (res
->preloadResult() == CachedObject::PreloadNotReferenced
|| res
->hadError())
1111 Cache::removeCacheEntry(res
);
1116 void DocLoader::printPreloadStats()
1118 #ifdef PRELOAD_DEBUG
1119 unsigned scripts
= 0;
1120 unsigned scriptMisses
= 0;
1121 unsigned stylesheets
= 0;
1122 unsigned stylesheetMisses
= 0;
1123 unsigned images
= 0;
1124 unsigned imageMisses
= 0;
1125 QSet
<CachedObject
*>::iterator end
= m_preloads
.end();
1126 for (QSet
<CachedObject
*>::iterator it
= m_preloads
.begin(); it
!= end
; ++it
) {
1127 CachedObject
* res
= *it
;
1128 if (res
->preloadResult() == CachedObject::PreloadNotReferenced
)
1129 fprintf(stderr
,"!! UNREFERENCED PRELOAD %s\n", res
->url().string().toLatin1().data());
1130 else if (res
->preloadResult() == CachedObject::PreloadReferencedWhileComplete
)
1131 fprintf(stderr
,"HIT COMPLETE PRELOAD %s\n", res
->url().string().toLatin1().data());
1132 else if (res
->preloadResult() == CachedObject::PreloadReferencedWhileLoading
)
1133 fprintf(stderr
,"HIT LOADING PRELOAD %s\n", res
->url().string().toLatin1().data());
1135 if (res
->type() == CachedObject::Script
) {
1137 if (res
->preloadResult() < CachedObject::PreloadReferencedWhileLoading
)
1139 } else if (res
->type() == CachedObject::CSSStyleSheet
) {
1141 if (res
->preloadResult() < CachedObject::PreloadReferencedWhileLoading
)
1145 if (res
->preloadResult() < CachedObject::PreloadReferencedWhileLoading
)
1150 fprintf(stderr
, "SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts
, scripts
- scriptMisses
, (scripts
- scriptMisses
) * 100 / scripts
);
1152 fprintf(stderr
, "STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets
, stylesheets
- stylesheetMisses
, (stylesheets
- stylesheetMisses
) * 100 / stylesheets
);
1154 fprintf(stderr
, "IMAGES: %d (%d hits, hit rate %d%%)\n", images
, images
- imageMisses
, (images
- imageMisses
) * 100 / images
);
1158 #define DOCLOADER_SECCHECK(doRedirectCheck) \
1159 KUrl fullURL (m_doc->completeURL( url.string() )); \
1160 if ( !fullURL.isValid() || \
1161 ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file" && fullURL.protocol() != "data") || \
1162 doRedirectCheck && (m_doc && !KAuthorized::authorizeUrlAction("redirect", m_doc->URL(), fullURL))) \
1165 CachedImage
*DocLoader::requestImage( const DOM::DOMString
&url
)
1167 DOCLOADER_SECCHECK(true);
1169 CachedImage
* i
= Cache::requestObject
<CachedImage
, CachedObject::Image
>( this, fullURL
, 0);
1171 if (i
&& i
->status() == CachedObject::Unknown
&& autoloadImages())
1172 Cache::loader()->load(this, i
, true);
1177 CachedCSSStyleSheet
*DocLoader::requestStyleSheet( const DOM::DOMString
&url
, const QString
& charset
,
1178 const char *accept
, bool userSheet
)
1180 DOCLOADER_SECCHECK(!userSheet
);
1182 CachedCSSStyleSheet
* s
= Cache::requestObject
<CachedCSSStyleSheet
, CachedObject::CSSStyleSheet
>( this, fullURL
, accept
);
1183 if ( s
&& !charset
.isEmpty() ) {
1184 s
->setCharsetHint( charset
);
1189 CachedScript
*DocLoader::requestScript( const DOM::DOMString
&url
, const QString
& charset
)
1191 DOCLOADER_SECCHECK(true);
1192 if ( ! KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(fullURL
.host()) ||
1193 KHTMLGlobal::defaultHTMLSettings()->isAdFiltered(fullURL
.url()))
1196 CachedScript
* s
= Cache::requestObject
<CachedScript
, CachedObject::Script
>( this, fullURL
, 0 );
1197 if ( s
&& !charset
.isEmpty() )
1198 s
->setCharset( charset
);
1202 CachedSound
*DocLoader::requestSound( const DOM::DOMString
&url
)
1204 DOCLOADER_SECCHECK(true);
1205 CachedSound
* s
= Cache::requestObject
<CachedSound
, CachedObject::Sound
>( this, fullURL
, 0 );
1209 #undef DOCLOADER_SECCHECK
1211 void DocLoader::setAutoloadImages( bool enable
)
1213 if ( enable
== m_bautoloadImages
)
1216 m_bautoloadImages
= enable
;
1218 if ( !m_bautoloadImages
) return;
1220 for ( QSetIterator
<CachedObject
*> it( m_docObjects
); it
.hasNext(); )
1222 CachedObject
* cur
= it
.next();
1223 if ( cur
->type() == CachedObject::Image
)
1225 CachedImage
*img
= const_cast<CachedImage
*>( static_cast<const CachedImage
*>(cur
) );
1227 CachedObject::Status status
= img
->status();
1228 if ( status
!= CachedObject::Unknown
)
1231 Cache::loader()->load(this, img
, true);
1236 void DocLoader::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations
)
1238 if ( showAnimations
== m_showAnimations
) return;
1239 m_showAnimations
= showAnimations
;
1241 for ( QSetIterator
<CachedObject
*> it( m_docObjects
); it
.hasNext(); )
1243 CachedObject
* cur
= it
.next();
1244 if ( cur
->type() == CachedObject::Image
)
1246 CachedImage
*img
= const_cast<CachedImage
*>( static_cast<const CachedImage
*>( cur
) );
1248 img
->setShowAnimations( m_showAnimations
);
1253 void DocLoader::pauseAnimations()
1255 for ( QSetIterator
<CachedObject
*> it( m_docObjects
); it
.hasNext(); )
1257 CachedObject
* cur
= it
.next();
1258 if ( cur
->type() == CachedObject::Image
)
1260 CachedImage
*img
= const_cast<CachedImage
*>( static_cast<const CachedImage
*>( cur
) );
1262 img
->pauseAnimations();
1267 void DocLoader::resumeAnimations()
1269 for ( QSetIterator
<CachedObject
*> it( m_docObjects
); it
.hasNext(); )
1271 CachedObject
* cur
= it
.next();
1272 if ( cur
->type() == CachedObject::Image
)
1274 CachedImage
*img
= const_cast<CachedImage
*>( static_cast<const CachedImage
*>( cur
) );
1276 img
->resumeAnimations();
1281 // ------------------------------------------------------------------------------------------
1283 Loader::Loader() : QObject()
1285 connect(&m_timer
, SIGNAL(timeout()), this, SLOT( servePendingRequests() ) );
1286 m_highPriorityRequestPending
= 0;
1291 delete m_highPriorityRequestPending
;
1292 qDeleteAll(m_requestsPending
);
1293 qDeleteAll(m_requestsLoading
);
1296 void Loader::load(DocLoader
* dl
, CachedObject
*object
, bool incremental
, bool highPriority
)
1298 Request
*req
= new Request(dl
, object
, incremental
);
1299 if (highPriority
&& !m_highPriorityRequestPending
) {
1300 m_highPriorityRequestPending
= req
;
1303 m_requestsPending
.prepend(req
);
1305 m_requestsPending
.append(req
);
1307 highPriority
= false;
1310 emit
requestStarted( req
->m_docLoader
, req
->object
);
1313 servePendingRequests();
1315 m_timer
.setSingleShot(true);
1320 void Loader::servePendingRequests()
1322 while ( (m_highPriorityRequestPending
!= 0 || m_requestsPending
.count() != 0) && (m_requestsLoading
.count() < MAX_JOB_COUNT
) )
1324 // get the first pending request
1325 Request
*req
= m_highPriorityRequestPending
? m_highPriorityRequestPending
: m_requestsPending
.takeFirst();
1328 kDebug( 6060 ) << "starting Loader url=" << req
->object
->url().string();
1331 KUrl
u(req
->object
->url().string());
1332 KIO::TransferJob
* job
= KIO::get( u
, KIO::NoReload
, KIO::HideProgressInfo
/*no GUI*/);
1334 job
->addMetaData("cache", KIO::getCacheControlString(req
->object
->cachePolicy()));
1335 if (!req
->object
->accept().isEmpty())
1336 job
->addMetaData("accept", req
->object
->accept());
1337 if ( req
->m_docLoader
)
1339 job
->addMetaData( "referrer", req
->m_docLoader
->doc()->URL().url() );
1341 KHTMLPart
*part
= req
->m_docLoader
->part();
1344 job
->addMetaData( "cross-domain", part
->toplevelURL().url() );
1346 job
->ui()->setWindow (part
->widget()->topLevelWidget());
1350 connect( job
, SIGNAL( result( KJob
* ) ), this, SLOT( slotFinished( KJob
* ) ) );
1351 connect( job
, SIGNAL( mimetype( KIO::Job
*, const QString
& ) ), this, SLOT( slotMimetype( KIO::Job
*, const QString
& ) ) );
1352 connect( job
, SIGNAL( data( KIO::Job
*, const QByteArray
&)),
1353 SLOT( slotData( KIO::Job
*, const QByteArray
&)));
1355 if ( req
->object
->schedule() )
1356 KIO::Scheduler::scheduleJob( job
);
1358 m_requestsLoading
.insertMulti(job
, req
);
1359 if (m_highPriorityRequestPending
) {
1360 m_highPriorityRequestPending
= 0;
1366 void Loader::slotMimetype( KIO::Job
*j
, const QString
& s
)
1368 KIO::TransferJob
* job
= static_cast<KIO::TransferJob
*>(j
);
1369 Request
*r
= m_requestsLoading
.value( j
);
1372 CachedObject
*o
= r
->object
;
1374 // Mozilla plain ignores any mimetype that doesn't have / in it, and handles it as "",
1375 // including when being picky about mimetypes. Match that for better compatibility with broken servers.
1376 if (s
.contains('/'))
1382 void Loader::slotFinished( KJob
* job
)
1384 KIO::TransferJob
* j
= static_cast<KIO::TransferJob
*>(job
);
1385 Request
*r
= m_requestsLoading
.take( j
);
1390 if (j
->error() || j
->isErrorPage())
1393 kDebug(6060) << "Loader::slotFinished, with error. job->error()= " << j
->error() << " job->isErrorPage()=" << j
->isErrorPage();
1395 r
->object
->error( job
->error(), job
->errorText().toAscii().constData() );
1396 emit
requestFailed( r
->m_docLoader
, r
->object
);
1400 QString cs
= j
->queryMetaData("charset");
1401 if (!cs
.isEmpty()) r
->object
->setCharset(cs
);
1402 r
->object
->data(r
->m_buffer
, true);
1403 emit
requestDone( r
->m_docLoader
, r
->object
);
1404 time_t expireDate
= j
->queryMetaData("expire-date").toLong();
1406 kDebug(6060) << "Loader::slotFinished, url = " << j
->url().url();
1408 r
->object
->setExpireDate( expireDate
);
1410 if ( r
->object
->type() == CachedObject::Image
) {
1411 QString fn
= j
->queryMetaData("content-disposition");
1412 static_cast<CachedImage
*>( r
->object
)->setSuggestedFilename(fn
);
1414 static_cast<CachedImage
*>( r
->object
)->setSuggestedTitle(fn
);
1417 tf
.write((const char*)r
->m_buffer
.buffer().data(), r
->m_buffer
.size());
1419 KFileMetaInfo
kfmi(tf
.fileName());
1420 if (!kfmi
.isEmpty()) {
1421 KFileMetaInfoItem i
= kfmi
.item("Name");
1423 static_cast<CachedImage
*>(r
->object
)->setSuggestedTitle(i
.string());
1425 i
= kfmi
.item("Title");
1427 static_cast<CachedImage
*>(r
->object
)->setSuggestedTitle(i
.string());
1435 r
->object
->finish();
1438 kDebug( 6060 ) << "Loader:: JOB FINISHED " << r
->object
<< ": " << r
->object
->url().string();
1443 if ( (m_highPriorityRequestPending
!= 0 || m_requestsPending
.count() != 0) && (m_requestsLoading
.count() < MAX_JOB_COUNT
/ 2) ) {
1444 m_timer
.setSingleShot(true);
1449 void Loader::slotData( KIO::Job
*job
, const QByteArray
&data
)
1451 Request
*r
= m_requestsLoading
[job
];
1453 kDebug( 6060 ) << "got data for unknown request!";
1457 if ( !r
->m_buffer
.isOpen() )
1458 r
->m_buffer
.open( QIODevice::WriteOnly
);
1460 r
->m_buffer
.write( data
.data(), data
.size() );
1463 r
->object
->data( r
->m_buffer
, false );
1466 int Loader::numRequests( DocLoader
* dl
) const
1469 if (m_highPriorityRequestPending
&& m_highPriorityRequestPending
->m_docLoader
== dl
)
1472 foreach( Request
* req
, m_requestsPending
)
1473 if ( req
->m_docLoader
== dl
)
1476 foreach( Request
* req
, m_requestsLoading
)
1477 if ( req
->m_docLoader
== dl
)
1483 void Loader::cancelRequests( DocLoader
* dl
)
1485 if (m_highPriorityRequestPending
&& m_highPriorityRequestPending
->m_docLoader
== dl
) {
1486 CDEBUG
<< "canceling high priority pending request for " << m_highPriorityRequestPending
->object
->url().string() << endl
;
1487 Cache::removeCacheEntry( m_highPriorityRequestPending
->object
);
1488 delete m_highPriorityRequestPending
;
1489 m_highPriorityRequestPending
= 0;
1492 QMutableLinkedListIterator
<Request
*> pIt( m_requestsPending
);
1493 while ( pIt
.hasNext() ) {
1494 Request
* cur
= pIt
.next();
1495 if ( cur
->m_docLoader
== dl
)
1497 CDEBUG
<< "canceling pending request for " << cur
->object
->url().string() << endl
;
1498 Cache::removeCacheEntry( cur
->object
);
1504 //kDebug( 6060 ) << "got " << m_requestsLoading.count() << "loading requests";
1506 QMutableHashIterator
<KIO::Job
*,Request
*> lIt( m_requestsLoading
);
1507 while ( lIt
.hasNext() )
1510 if ( lIt
.value()->m_docLoader
== dl
)
1512 //kDebug( 6060 ) << "canceling loading request for " << lIt.current()->object->url().string();
1513 KIO::Job
*job
= static_cast<KIO::Job
*>( lIt
.key() );
1514 Cache::removeCacheEntry( lIt
.value()->object
);
1522 KIO::Job
*Loader::jobForRequest( const DOM::DOMString
&url
) const
1524 QHashIterator
<KIO::Job
*,Request
*> it( m_requestsLoading
);
1525 while (it
.hasNext())
1528 if ( it
.value()->object
&& it
.value()->object
->url() == url
)
1529 return static_cast<KIO::Job
*>( it
.key() );
1535 // ----------------------------------------------------------------------------
1538 QHash
<QString
,CachedObject
*> *Cache::cache
;
1539 QLinkedList
<DocLoader
*> *Cache::docloader
;
1540 QLinkedList
<CachedObject
*> *Cache::freeList
;
1541 Loader
*Cache::m_loader
;
1543 int Cache::maxSize
= DEFCACHESIZE
;
1544 int Cache::totalSizeOfLRU
;
1546 QPixmap
*Cache::nullPixmap
;
1547 QPixmap
*Cache::brokenPixmap
;
1548 QPixmap
*Cache::blockedPixmap
;
1553 cache
= new QHash
<QString
,CachedObject
*>();
1556 docloader
= new QLinkedList
<DocLoader
*>;
1559 nullPixmap
= new QPixmap
;
1561 if ( !brokenPixmap
)
1562 brokenPixmap
= new QPixmap(KHTMLGlobal::iconLoader()->loadIcon("image-missing", KIconLoader::Desktop
, 16, KIconLoader::DisabledState
));
1564 if ( !blockedPixmap
) {
1565 blockedPixmap
= new QPixmap();
1566 blockedPixmap
->loadFromData(blocked_icon_data
, blocked_icon_len
);
1570 m_loader
= new Loader();
1573 freeList
= new QLinkedList
<CachedObject
*>;
1578 if ( !cache
) return;
1580 kDebug( 6060 ) << "Cache: CLEAR!";
1586 foreach (CachedObject
* co
, *cache
) {
1587 if (!co
->canDelete()) {
1588 kDebug( 6060 ) << " Object in cache still linked to";
1589 kDebug( 6060 ) << " -> URL: " << co
->url();
1590 kDebug( 6060 ) << " -> #clients: " << co
->count();
1592 // assert(co->canDelete());
1595 foreach (CachedObject
* co
, *freeList
) {
1596 if (!co
->canDelete()) {
1597 kDebug( 6060 ) << " Object in freelist still linked to";
1598 kDebug( 6060 ) << " -> URL: " << co
->url();
1599 kDebug( 6060 ) << " -> #clients: " << co
->count();
1602 foreach (CachedObjectClient* cur, (*co->m_clients)))
1604 if (dynamic_cast<RenderObject*>(cur)) {
1605 kDebug( 6060 ) << " --> RenderObject";
1607 kDebug( 6060 ) << " --> Something else";
1610 // assert(freeList->current()->canDelete());
1615 delete cache
; cache
= 0;
1616 delete nullPixmap
; nullPixmap
= 0;
1617 delete brokenPixmap
; brokenPixmap
= 0;
1618 delete blockedPixmap
; blockedPixmap
= 0;
1619 delete m_loader
; m_loader
= 0;
1620 delete docloader
; docloader
= 0;
1621 qDeleteAll(*freeList
);
1622 delete freeList
; freeList
= 0;
1625 template<typename CachedObjectType
, enum CachedObject::Type CachedType
>
1626 CachedObjectType
* Cache::requestObject( DocLoader
* dl
, const KUrl
& kurl
, const char* accept
)
1628 KIO::CacheControl cachePolicy
= dl
->cachePolicy();
1630 QString url
= kurl
.url();
1631 CachedObject
* o
= cache
->value(url
);
1633 if ( o
&& o
->type() != CachedType
) {
1634 removeCacheEntry( o
);
1638 if ( o
&& dl
->needReload( o
, url
) ) {
1640 assert( !cache
->contains( url
) );
1646 kDebug( 6060 ) << "Cache: new: " << kurl
.url();
1648 CachedObjectType
* cot
= new CachedObjectType(dl
, url
, cachePolicy
, accept
);
1649 cache
->insert( url
, cot
);
1650 if ( cot
->allowInLRUList() )
1651 insertInLRUList( cot
);
1656 kDebug( 6060 ) << "Cache: using pending/cached: " << kurl
.url();
1661 dl
->insertCachedObject( o
);
1663 return static_cast<CachedObjectType
*>(o
);
1666 void Cache::preloadStyleSheet( const QString
&url
, const QString
&stylesheet_data
)
1668 if (cache
->contains(url
))
1669 removeCacheEntry(cache
->value(url
));
1671 CachedCSSStyleSheet
*stylesheet
= new CachedCSSStyleSheet(url
, stylesheet_data
);
1672 cache
->insert( url
, stylesheet
);
1675 void Cache::preloadScript( const QString
&url
, const QString
&script_data
)
1677 if (cache
->contains(url
))
1678 removeCacheEntry(cache
->value(url
));
1680 CachedScript
*script
= new CachedScript(url
, script_data
);
1681 cache
->insert( url
, script
);
1684 void Cache::flush(bool force
)
1688 if ( force
|| totalSizeOfLRU
> maxSize
+ maxSize
/4) {
1689 for ( int i
= MAX_LRU_LISTS
-1; i
>= 0 && totalSizeOfLRU
> maxSize
; --i
)
1690 while ( totalSizeOfLRU
> maxSize
&& m_LRULists
[i
].m_tail
)
1691 removeCacheEntry( m_LRULists
[i
].m_tail
);
1698 QMutableLinkedListIterator
<CachedObject
*> it(*freeList
);
1699 while ( it
.hasNext() ) {
1700 CachedObject
* p
= it
.next();
1701 if ( p
->canDelete() ) {
1708 void Cache::setSize( int bytes
)
1711 flush(true /* force */);
1714 void Cache::statistics()
1716 // this function is for debugging purposes only
1724 int stylesheets
= 0;
1726 foreach (CachedObject
* o
, *cache
)
1729 case CachedObject::Image
:
1731 //CachedImage *im = static_cast<CachedImage *>(o);
1736 msize += im->size();
1740 case CachedObject::CSSStyleSheet
:
1743 case CachedObject::Script
:
1746 case CachedObject::Sound
:
1754 kDebug( 6060 ) << "------------------------- image cache statistics -------------------";
1755 kDebug( 6060 ) << "Number of items in cache: " << cache
->count();
1756 kDebug( 6060 ) << "Number of cached images: " << images
;
1757 kDebug( 6060 ) << "Number of cached movies: " << movie
;
1758 kDebug( 6060 ) << "Number of cached scripts: " << scripts
;
1759 kDebug( 6060 ) << "Number of cached stylesheets: " << stylesheets
;
1760 kDebug( 6060 ) << "Number of cached sounds: " << sound
;
1761 kDebug( 6060 ) << "pixmaps: allocated space approx. " << size
<< " kB";
1762 kDebug( 6060 ) << "movies : allocated space approx. " << msize
/1024 << " kB";
1763 kDebug( 6060 ) << "--------------------------------------------------------------------";
1766 void Cache::removeCacheEntry( CachedObject
*object
)
1768 QString key
= object
->url().string();
1770 cache
->remove( key
);
1771 removeFromLRUList( object
);
1773 foreach( DocLoader
* dl
, *docloader
)
1774 dl
->removeCachedObject( object
);
1776 if ( !object
->free() ) {
1777 Cache::freeList
->append( object
);
1778 object
->m_free
= true;
1782 static inline int FastLog2(unsigned int j
)
1789 log2
+= 16, j
>>= 16;
1802 static LRUList
* getLRUListFor(CachedObject
* o
)
1804 int accessCount
= o
->accessCount();
1806 if (accessCount
== 0) {
1809 int sizeLog
= FastLog2(o
->size());
1810 queueIndex
= sizeLog
/o
->accessCount() - 1;
1813 if (queueIndex
>= MAX_LRU_LISTS
)
1814 queueIndex
= MAX_LRU_LISTS
-1;
1816 return &m_LRULists
[queueIndex
];
1819 void Cache::removeFromLRUList(CachedObject
*object
)
1821 CachedObject
*next
= object
->m_next
;
1822 CachedObject
*prev
= object
->m_prev
;
1824 LRUList
* list
= getLRUListFor(object
);
1825 CachedObject
*&head
= getLRUListFor(object
)->m_head
;
1827 if (next
== 0 && prev
== 0 && head
!= object
) {
1835 next
->m_prev
= prev
;
1836 else if (list
->m_tail
== object
)
1837 list
->m_tail
= prev
;
1840 prev
->m_next
= next
;
1841 else if (head
== object
)
1844 totalSizeOfLRU
-= object
->size();
1847 void Cache::insertInLRUList(CachedObject
*object
)
1849 removeFromLRUList(object
);
1852 assert( !object
->free() );
1853 assert( object
->canDelete() );
1854 assert( object
->allowInLRUList() );
1856 LRUList
* list
= getLRUListFor(object
);
1858 CachedObject
*&head
= list
->m_head
;
1860 object
->m_next
= head
;
1862 head
->m_prev
= object
;
1865 if (object
->m_next
== 0)
1866 list
->m_tail
= object
;
1868 totalSizeOfLRU
+= object
->size();
1871 // --------------------------------------
1873 void CachedObjectClient::updatePixmap(const QRect
&, CachedImage
*) {}
1874 void CachedObjectClient::setStyleSheet(const DOM::DOMString
&/*url*/, const DOM::DOMString
&/*sheet*/, const DOM::DOMString
&/*charset*/, const DOM::DOMString
&/*mimetype*/) {}
1875 void CachedObjectClient::notifyFinished(CachedObject
* /*finishedObj*/) {}
1876 void CachedObjectClient::error(int /*err*/, const QString
&/*text*/) {}
1880 #include "loader.moc"