fix logic
[personal-kdelibs.git] / khtml / misc / loader.cpp
blob72568a47d761e8e7946105c6891d0cd811206832
1 /*
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.
27 // regarding the LRU:
28 // http://www.is.kyusan-u.ac.jp/~chengk/pub/papers/compsac00_A07-07.pdf
31 #undef CACHE_DEBUG
32 //#define CACHE_DEBUG
34 #ifdef CACHE_DEBUG
35 #define CDEBUG kDebug(6060)
36 #else
37 #define CDEBUG kDebugDevNull()
38 #endif
40 #undef LOADER_DEBUG
41 //#define LOADER_DEBUG
43 //#define PRELOAD_DEBUG
45 #include "loader.h"
46 #include "seed.h"
47 #include <imload/image.h>
48 #include <imload/imagepainter.h>
50 #include <assert.h>
52 // default cache size
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>
64 #include <kio/job.h>
65 #include <kio/jobuidelegate.h>
66 #include <kio/jobclasses.h>
67 #include <kglobal.h>
68 #include <kcharsets.h>
69 #include <kiconloader.h>
70 #include <scheduler.h>
71 #include <kdebug.h>
73 #include <khtml_global.h>
74 #include <khtml_part.h>
76 #ifdef IMAGE_TITLES
77 #include <qfile.h>
78 #include <kfilemetainfo.h>
79 #include <ktemporaryfile.h>
80 #endif
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;
89 using namespace DOM;
90 using namespace khtmlImLoad;
92 #define MAX_LRU_LISTS 20
93 struct LRUList {
94 CachedObject* m_head;
95 CachedObject* m_tail;
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()
114 m_status = Cached;
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 )
127 m_status = Pending;
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) {
141 if (isLoaded())
142 m_preloadResult = PreloadReferencedWhileComplete;
143 else if (m_prospectiveRequest)
144 m_preloadResult = PreloadReferencedWhileLoading;
145 else
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);
154 m_accessCount++;
157 void CachedObject::deref(CachedObjectClient *c)
159 assert( c );
160 assert( m_clients.count() );
161 assert( !canDelete() );
162 assert( m_clients.contains( c ) );
164 Cache::flush();
166 m_clients.take(c);
168 if (allowInLRUList())
169 Cache::insertInLRUList(this);
172 void CachedObject::setSize(int size)
174 bool sizeChanged;
176 if ( !m_next && !m_prev && getLRUListFor(this)->m_head != this )
177 sizeChanged = false;
178 else
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);
186 m_size = size;
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();
200 // BOM
201 if ( s >= 3 &&
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
209 // Link or @charset
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");
217 return c;
220 // Default
221 return QTextCodec::codecForMib( 4 ); // latin 1
224 // -------------------------------------------------------------------------------------------
226 CachedCSSStyleSheet::CachedCSSStyleSheet(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy,
227 const char *accept)
228 : CachedObject(url, CSSStyleSheet, _cachePolicy, 0)
230 // Set the type we want (probably css or xml)
231 QString ah = QLatin1String( accept );
232 if ( !ah.isEmpty() )
233 ah += ',';
234 ah += "*/*;q=0.1";
235 setAccept( ah );
236 m_hadError = false;
237 m_wasBlocked = false;
238 m_err = 0;
239 // load the file
240 Cache::loader()->load(dl, this, false, true /*highPriority*/);
241 m_loading = true;
244 CachedCSSStyleSheet::CachedCSSStyleSheet(const DOMString &url, const QString &stylesheet_data)
245 : CachedObject(url, CSSStyleSheet, KIO::CC_Verify, stylesheet_data.length())
247 m_loading = false;
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);
262 if (!m_loading) {
263 if (m_hadError)
264 c->error( m_err, m_errText );
265 else
266 c->setStyleSheet( m_url, m_sheet, m_charset, m_mimetype );
270 void CachedCSSStyleSheet::data( QBuffer &buffer, bool eof )
272 if(!eof) return;
273 buffer.close();
274 setSize(buffer.buffer().size());
276 // QString charset = checkCharset( buffer.buffer() );
277 QTextCodec* c = 0;
278 if (!m_charset.isEmpty()) {
279 c = KGlobal::charsets()->codecForName(m_charset);
280 if(c->mibEnum() == 11) c = QTextCodec::codecForName("iso8859-8-i");
282 else {
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);
289 m_loading = false;
291 checkNotify();
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 )
307 m_hadError = true;
308 m_err = err;
309 m_errText = text;
310 m_loading = false;
312 for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
313 it.next().value()->error( m_err, m_errText );
316 #if 0
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));
332 return charset;
334 return m_charset;
336 #endif
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("*/*") );
347 // load the file
348 Cache::loader()->load(dl, this, false);
349 m_loading = true;
350 m_hadError = false;
353 CachedScript::CachedScript(const DOMString &url, const QString &script_data)
354 : CachedObject(url, Script, KIO::CC_Verify, script_data.length())
356 m_hadError = false;
357 m_loading = false;
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 )
371 if(!eof) return;
372 buffer.close();
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);
378 m_loading = false;
379 checkNotify();
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*/ )
392 m_hadError = true;
393 m_loading = false;
394 checkNotify();
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);
412 //p = 0;
413 //pixPart = 0;
414 bg = 0;
415 scaled = 0;
416 bgColor = qRgba( 0, 0, 0, 0 );
417 typeChecked = false;
418 isFullyTransparent = false;
419 monochrome = false;
420 formatType = 0;
421 m_status = Unknown;
422 imgSource = 0;
423 setAccept( acceptHeader );
424 i->setShowAnimations(dl->showAnimations());
425 m_loading = true;
427 if ( KHTMLGlobal::defaultHTMLSettings()->isAdFiltered( url.string() ) ) {
428 m_wasBlocked = true;
429 CachedObject::finish();
433 CachedImage::~CachedImage()
435 clear();
436 delete i;
439 void CachedImage::ref( CachedObjectClient *c )
441 CachedObject::ref(c);
443 #ifdef LOADER_DEBUG
444 kDebug(6060) << " image "<<this<<" ref'd by client " << c << "\n";
445 #endif
447 // for mouseovers, dynamic changes
448 //### having both makes no sense
449 if ( m_status >= Persistent && !pixmap_size().isNull() ) {
450 #ifdef LOADER_DEBUG
451 kDebug(6060) << "Notifying finished size:" <<
452 i->size().width() << ", " << i->size().height() << endl;
453 #endif
454 c->updatePixmap( QRect(QPoint(0, 0), pixmap_size()),
455 this );
456 c->notifyFinished( this );
460 void CachedImage::deref( CachedObjectClient *c )
462 CachedObject::deref(c);
463 /* if(m && m_clients.isEmpty() && m->running())
464 m->pause();*/
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;
480 #ifdef __GNUC__
481 #warning "Needs some additional performance work"
482 #endif
484 static QRgb bgTransparent = qRgba( 0, 0, 0, 0 );
486 QSize s(pixmap_size());
487 int w = xWidth;
488 int h = xHeight;
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)) )
496 delete bg; bg = 0;
499 if (bg)
500 return *bg;
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);
510 } else {
511 src = &r;
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);
521 bg->fill(newc);
522 QPainter p(bg);
523 p.drawPixmap(0, 0, *src);
524 bgColor = newc.rgba();
525 src = bg;
526 } else {
527 bgColor = bgTransparent;
530 //See whether to pre-tile.
531 if ( w*h < 8192 )
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;
542 QPixmap* oldbg = bg;
543 bg = new QPixmap(w, h);
544 if (src->hasAlpha() || src->hasAlphaChannel()) {
545 if (newc.isValid() && (bgColor != bgTransparent))
546 bg->fill( bgColor );
547 else
548 bg->fill( Qt::transparent );
551 QPainter p(bg);
552 p.drawTiledPixmap(0, 0, w, h, *src);
553 p.end();
555 if ( src == oldbg )
556 delete oldbg;
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);
568 if (bg)
569 return *bg;
571 return *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;
584 if (scaled) {
585 if (scaled->width() == xWidth && scaled->height() == xHeight)
586 return scaled;
587 delete scaled;
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);
594 QPainter paint(&im);
595 paint.setCompositionMode(QPainter::CompositionMode_Source);
596 ImagePainter pi(i, QSize(xWidth, xHeight));
597 pi.paint(0, 0, &paint);
598 paint.end();
600 scaled = new QPixmap(QPixmap::fromImage(im));
602 return scaled;
605 QPixmap CachedImage::pixmap( ) const
607 if (m_hadError)
608 return *Cache::brokenPixmap;
610 if(m_wasBlocked)
611 return *Cache::blockedPixmap;
613 int w = i->size().width();
614 int h = i->size().height();
616 if (i->hasAlpha()) {
617 QImage im(w, h, QImage::Format_ARGB32_Premultiplied);
619 QPainter paint(&im);
620 paint.setCompositionMode(QPainter::CompositionMode_Source);
621 ImagePainter pi(i);
622 pi.paint(0, 0, &paint);
623 paint.end();
624 return QPixmap::fromImage( im );
625 } else {
626 QPixmap pm(w, h);
627 QPainter paint(&pm);
628 ImagePainter pi(i);
629 pi.paint(0, 0, &paint);
630 paint.end();
631 return pm;
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();
641 return QSize();
645 void CachedImage::imageHasGeometry(khtmlImLoad::Image* /*img*/, int width, int height)
647 #ifdef LOADER_DEBUG
648 kDebug(6060) << this << " got geometry "<< width << "x" << height;
649 #endif
650 do_notify(QRect(0, 0, width, height));
653 void CachedImage::imageChange (khtmlImLoad::Image* /*img*/, QRect region)
655 #ifdef LOADER_DEBUG
656 kDebug(6060) << "Image " << this << " change " <<
657 region.x() << "," << region.y() << ":" << region.width() << "x" << region.height() << endl;
658 #endif
659 //### this is overly conservative -- I guess we need to also specify reason,
660 //e.g. repaint vs. changed !!!
661 delete bg;
662 bg = 0;
664 do_notify(region);
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*/)
677 error(0, 0);
681 void CachedImage::imageDone(khtmlImLoad::Image* /*img*/)
683 #ifdef LOADER_DEBUG
684 kDebug(6060)<<"Image is done:" << this;
685 #endif
686 m_status = Persistent;
687 m_loading = false;
688 doNotifyFinished();
691 // QRect CachedImage::valid_rect() const
692 // {
693 // if (m_wasBlocked) return Cache::blockedPixmap->rect();
694 // return (m_hadError ? Cache::brokenPixmap->rect() : m ? m->frameRect() : ( p ? p->rect() : QRect()) );
695 // }
698 void CachedImage::do_notify(const QRect& r)
700 for (QHashIterator<CachedObjectClient*,CachedObjectClient*> it( m_clients ); it.hasNext();)
702 #ifdef LOADER_DEBUG
703 kDebug(6060) << " image "<<this<<" notify of geom client " << it.peekNext() << "\n";
704 #endif
705 it.next().value()->updatePixmap( r, this);
710 // void CachedImage::movieUpdated( const QRect& r )
711 // {
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());
715 // #endif
717 // do_notify(m->framePixmap(), r);
718 // }
719 #if 0
721 void CachedImage::movieStatus(int status)
723 #warning QMovie emits different signals now and requires different ways to call
724 #ifdef LOADER_DEBUG
725 qDebug("movieStatus(%d)", status);
726 #endif
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))
741 monochrome = false;
742 if( (im.width() < 5 || im.height() < 5) && im.hasAlphaBuffer()) // only evaluate for small images
744 QImage am = im.createAlphaMask();
745 if(am.depth() == 1)
747 bool solid = false;
748 for(int y = 0; y < am.height(); y++)
749 for(int x = 0; x < am.width(); x++)
750 if(am.pixelIndex(x, y)) {
751 solid = true;
752 break;
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)
760 delete bg;
761 bg = 0;
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))
769 if(imgSource)
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() );
780 delete p;
781 p = pix;
782 monochrome = false;
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))
792 #ifdef LOADER_DEBUG
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());
796 #endif
797 do_notify(pixmap(), valid_rect());
800 #endif
802 // void CachedImage::movieResize(const QSize& /*s*/)
803 // {
804 // do_notify(m->framePixmap(), QRect());
805 // }
807 void CachedImage::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
809 if (i)
810 i->setShowAnimations(showAnimations);
813 // void CachedImage::deleteMovie()
814 // {
815 // delete m; m = 0;
816 // }
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 );
833 delete bg; bg = 0;
834 bgSize = QSize(-1,-1);
836 formatType = 0;
837 typeChecked = false;
838 setSize(0);
840 // No need to delete imageSource - QMovie does it for us
841 imgSource = 0;
844 void CachedImage::data ( QBuffer &_buffer, bool eof )
846 #ifdef LOADER_DEBUG
847 kDebug( 6060 ) << this << "in CachedImage::data(buffersize " << _buffer.buffer().size() <<", eof=" << eof << " pos:" << _buffer.pos();
848 #endif
849 i->processData((uchar*)_buffer.data().data(), _buffer.pos());
851 _buffer.close();
853 if (eof)
854 i->processEOF();
856 //if ( !typeChecked )
858 // don't attempt incremental loading if we have all the data already
859 // assert(!eof);
861 #ifdef __GNUC__
862 #warning QImage* requires heavy porting
863 #endif
864 #if 0
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
869 typeChecked = true;
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& ) ) );
882 if ( imgSource )
884 imgSource->setEOF(eof);
885 imgSource->maybeReady();
888 if(eof)
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)
895 #ifdef CACHE_DEBUG
896 kDebug(6060) << "CachedImage::data(): reloading as pixmap:";
897 #endif
898 p = new QPixmap;
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.
909 #ifdef CACHE_DEBUG
910 kDebug(6060) << "CachedImage::data(): image is null: " << p->isNull();
911 #endif
912 if(p->isNull())
914 m_hadError = true;
915 do_notify(pixmap(), QRect(0, 0, 16, 16)); // load "broken image" icon
917 else
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
924 #endif
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() );
936 m_loading = false;
937 QSize s = pixmap_size();
938 setSize( s.width() * s.height() * 2);
942 void CachedImage::error( int /*err*/, const char* /*text*/ )
944 clear();
945 typeChecked = true;
946 m_hadError = true;
947 m_loading = false;
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);
960 m_loading = true;
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 )
972 if(!eof) return;
973 buffer.close();
974 setSize(buffer.buffer().size());
976 m_sound = buffer.buffer();
977 m_loading = false;
978 checkNotify();
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*/ )
991 m_loading = false;
992 checkNotify();
995 // ------------------------------------------------------------------------------------------
997 Request::Request(DocLoader* dl, CachedObject *_object, bool _incremental)
999 object = _object;
1000 object->setRequest(this);
1001 incremental = _incremental;
1002 m_docLoader = dl;
1005 Request::~Request()
1007 object->setRequest(0);
1010 // ------------------------------------------------------------------------------------------
1012 DocLoader::DocLoader(KHTMLPart* part, DocumentImpl* doc)
1014 m_cachePolicy = KIO::CC_Verify;
1015 m_expireDate = 0;
1016 m_creationDate = time(0);
1017 m_bautoloadImages = true;
1018 m_showAnimations = KHTMLSettings::KAnimationEnabled;
1019 m_part = part;
1020 m_doc = doc;
1022 Cache::docloader->append( this );
1025 DocLoader::~DocLoader()
1027 clearPreloads();
1028 Cache::loader()->cancelRequests( this );
1029 Cache::docloader->removeAll( this );
1032 void DocLoader::setCacheCreationDate(time_t _creationDate)
1034 if (_creationDate)
1035 m_creationDate = _creationDate;
1036 else
1037 m_creationDate = time(0); // Now
1040 void DocLoader::setExpireDate(time_t _expireDate, bool relative)
1042 if (relative)
1043 m_expireDate = _expireDate + m_creationDate; // Relative date
1044 else
1045 m_expireDate = _expireDate; // Absolute date
1046 #ifdef CACHE_DEBUG
1047 kDebug(6061) << "docLoader: " << m_expireDate - time(0) << " seconds left until reload required.\n";
1048 #endif
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);
1070 reload = true;
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);
1084 reload = true;
1088 return reload;
1091 void DocLoader::registerPreload(CachedObject* resource)
1093 if (!resource || resource->isLoaded() || m_preloads.contains(resource))
1094 return;
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());
1100 #endif
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);
1113 m_preloads.clear();
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) {
1136 scripts++;
1137 if (res->preloadResult() < CachedObject::PreloadReferencedWhileLoading)
1138 scriptMisses++;
1139 } else if (res->type() == CachedObject::CSSStyleSheet) {
1140 stylesheets++;
1141 if (res->preloadResult() < CachedObject::PreloadReferencedWhileLoading)
1142 stylesheetMisses++;
1143 } else {
1144 images++;
1145 if (res->preloadResult() < CachedObject::PreloadReferencedWhileLoading)
1146 imageMisses++;
1149 if (scripts)
1150 fprintf(stderr, "SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
1151 if (stylesheets)
1152 fprintf(stderr, "STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
1153 if (images)
1154 fprintf(stderr, "IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
1155 #endif
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))) \
1163 return 0L;
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);
1174 return i;
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 );
1186 return s;
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()))
1194 return 0L;
1196 CachedScript* s = Cache::requestObject<CachedScript, CachedObject::Script>( this, fullURL, 0 );
1197 if ( s && !charset.isEmpty() )
1198 s->setCharset( charset );
1199 return s;
1202 CachedSound *DocLoader::requestSound( const DOM::DOMString &url )
1204 DOCLOADER_SECCHECK(true);
1205 CachedSound* s = Cache::requestObject<CachedSound, CachedObject::Sound>( this, fullURL, 0 );
1206 return s;
1209 #undef DOCLOADER_SECCHECK
1211 void DocLoader::setAutoloadImages( bool enable )
1213 if ( enable == m_bautoloadImages )
1214 return;
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 )
1229 continue;
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;
1289 Loader::~Loader()
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;
1301 } else {
1302 if (highPriority) {
1303 m_requestsPending.prepend(req);
1304 } else {
1305 m_requestsPending.append(req);
1307 highPriority = false;
1310 emit requestStarted( req->m_docLoader, req->object );
1312 if (highPriority) {
1313 servePendingRequests();
1314 } else {
1315 m_timer.setSingleShot(true);
1316 m_timer.start(0);
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();
1327 #ifdef LOADER_DEBUG
1328 kDebug( 6060 ) << "starting Loader url=" << req->object->url().string();
1329 #endif
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();
1342 if (part )
1344 job->addMetaData( "cross-domain", part->toplevelURL().url() );
1345 if (part->widget())
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;
1361 break;
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 );
1370 if (!r)
1371 return;
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('/'))
1377 o->m_mimetype = s;
1378 else
1379 o->m_mimetype = "";
1382 void Loader::slotFinished( KJob* job )
1384 KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
1385 Request *r = m_requestsLoading.take( j );
1387 if ( !r )
1388 return;
1390 if (j->error() || j->isErrorPage())
1392 #ifdef LOADER_DEBUG
1393 kDebug(6060) << "Loader::slotFinished, with error. job->error()= " << j->error() << " job->isErrorPage()=" << j->isErrorPage();
1394 #endif
1395 r->object->error( job->error(), job->errorText().toAscii().constData() );
1396 emit requestFailed( r->m_docLoader, r->object );
1398 else
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();
1405 #ifdef LOADER_DEBUG
1406 kDebug(6060) << "Loader::slotFinished, url = " << j->url().url();
1407 #endif
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);
1413 #ifdef IMAGE_TITLES
1414 static_cast<CachedImage*>( r->object )->setSuggestedTitle(fn);
1415 KTemporaryFile tf;
1416 tf.open();
1417 tf.write((const char*)r->m_buffer.buffer().data(), r->m_buffer.size());
1418 tf.flush();
1419 KFileMetaInfo kfmi(tf.fileName());
1420 if (!kfmi.isEmpty()) {
1421 KFileMetaInfoItem i = kfmi.item("Name");
1422 if (i.isValid()) {
1423 static_cast<CachedImage*>(r->object)->setSuggestedTitle(i.string());
1424 } else {
1425 i = kfmi.item("Title");
1426 if (i.isValid()) {
1427 static_cast<CachedImage*>(r->object)->setSuggestedTitle(i.string());
1431 #endif
1435 r->object->finish();
1437 #ifdef LOADER_DEBUG
1438 kDebug( 6060 ) << "Loader:: JOB FINISHED " << r->object << ": " << r->object->url().string();
1439 #endif
1441 delete r;
1443 if ( (m_highPriorityRequestPending != 0 || m_requestsPending.count() != 0) && (m_requestsLoading.count() < MAX_JOB_COUNT / 2) ) {
1444 m_timer.setSingleShot(true);
1445 m_timer.start(0);
1449 void Loader::slotData( KIO::Job*job, const QByteArray &data )
1451 Request *r = m_requestsLoading[job];
1452 if(!r) {
1453 kDebug( 6060 ) << "got data for unknown request!";
1454 return;
1457 if ( !r->m_buffer.isOpen() )
1458 r->m_buffer.open( QIODevice::WriteOnly );
1460 r->m_buffer.write( data.data(), data.size() );
1462 if(r->incremental)
1463 r->object->data( r->m_buffer, false );
1466 int Loader::numRequests( DocLoader* dl ) const
1468 int res = 0;
1469 if (m_highPriorityRequestPending && m_highPriorityRequestPending->m_docLoader == dl)
1470 res++;
1472 foreach( Request* req, m_requestsPending )
1473 if ( req->m_docLoader == dl )
1474 res++;
1476 foreach( Request* req, m_requestsLoading)
1477 if ( req->m_docLoader == dl )
1478 res++;
1480 return res;
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 );
1499 pIt.remove();
1500 delete cur;
1504 //kDebug( 6060 ) << "got " << m_requestsLoading.count() << "loading requests";
1506 QMutableHashIterator<KIO::Job*,Request*> lIt( m_requestsLoading );
1507 while ( lIt.hasNext() )
1509 lIt.next();
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 );
1515 delete lIt.value();
1516 lIt.remove();
1517 job->kill();
1522 KIO::Job *Loader::jobForRequest( const DOM::DOMString &url ) const
1524 QHashIterator<KIO::Job*,Request*> it( m_requestsLoading );
1525 while (it.hasNext())
1527 it.next();
1528 if ( it.value()->object && it.value()->object->url() == url )
1529 return static_cast<KIO::Job *>( it.key() );
1532 return 0;
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;
1550 void Cache::init()
1552 if ( !cache )
1553 cache = new QHash<QString,CachedObject*>();
1555 if ( !docloader )
1556 docloader = new QLinkedList<DocLoader*>;
1558 if ( !nullPixmap )
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);
1569 if ( !m_loader )
1570 m_loader = new Loader();
1572 if ( !freeList )
1573 freeList = new QLinkedList<CachedObject*>;
1576 void Cache::clear()
1578 if ( !cache ) return;
1579 #ifdef CACHE_DEBUG
1580 kDebug( 6060 ) << "Cache: CLEAR!";
1581 statistics();
1582 #endif
1584 #ifndef NDEBUG
1585 bool crash = false;
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();
1591 crash = true;
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();
1600 crash = true;
1602 foreach (CachedObjectClient* cur, (*co->m_clients)))
1604 if (dynamic_cast<RenderObject*>(cur)) {
1605 kDebug( 6060 ) << " --> RenderObject";
1606 } else
1607 kDebug( 6060 ) << " --> Something else";
1610 // assert(freeList->current()->canDelete());
1612 assert(!crash);
1613 #endif
1614 qDeleteAll(*cache);
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 );
1635 o = 0;
1638 if ( o && dl->needReload( o, url ) ) {
1639 o = 0;
1640 assert( !cache->contains( url ) );
1643 if(!o)
1645 #ifdef CACHE_DEBUG
1646 kDebug( 6060 ) << "Cache: new: " << kurl.url();
1647 #endif
1648 CachedObjectType* cot = new CachedObjectType(dl, url, cachePolicy, accept);
1649 cache->insert( url, cot );
1650 if ( cot->allowInLRUList() )
1651 insertInLRUList( cot );
1652 o = cot;
1654 #ifdef CACHE_DEBUG
1655 else {
1656 kDebug( 6060 ) << "Cache: using pending/cached: " << kurl.url();
1658 #endif
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)
1686 init();
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 );
1693 #ifdef CACHE_DEBUG
1694 statistics();
1695 #endif
1698 QMutableLinkedListIterator<CachedObject*> it(*freeList);
1699 while ( it.hasNext() ) {
1700 CachedObject* p = it.next();
1701 if ( p->canDelete() ) {
1702 it.remove();
1703 delete p;
1708 void Cache::setSize( int bytes )
1710 maxSize = bytes;
1711 flush(true /* force */);
1714 void Cache::statistics()
1716 // this function is for debugging purposes only
1717 init();
1719 int size = 0;
1720 int msize = 0;
1721 int movie = 0;
1722 int images = 0;
1723 int scripts = 0;
1724 int stylesheets = 0;
1725 int sound = 0;
1726 foreach (CachedObject* o, *cache)
1728 switch(o->type()) {
1729 case CachedObject::Image:
1731 //CachedImage *im = static_cast<CachedImage *>(o);
1732 images++;
1733 /*if(im->m != 0)
1735 movie++;
1736 msize += im->size();
1738 break;
1740 case CachedObject::CSSStyleSheet:
1741 stylesheets++;
1742 break;
1743 case CachedObject::Script:
1744 scripts++;
1745 break;
1746 case CachedObject::Sound:
1747 sound++;
1748 break;
1750 size += o->size();
1752 size /= 1024;
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)
1784 unsigned int log2;
1785 log2 = 0;
1786 if (j & (j-1))
1787 log2 += 1;
1788 if (j >> 16)
1789 log2 += 16, j >>= 16;
1790 if (j >> 8)
1791 log2 += 8, j >>= 8;
1792 if (j >> 4)
1793 log2 += 4, j >>= 4;
1794 if (j >> 2)
1795 log2 += 2, j >>= 2;
1796 if (j >> 1)
1797 log2 += 1;
1799 return log2;
1802 static LRUList* getLRUListFor(CachedObject* o)
1804 int accessCount = o->accessCount();
1805 int queueIndex;
1806 if (accessCount == 0) {
1807 queueIndex = 0;
1808 } else {
1809 int sizeLog = FastLog2(o->size());
1810 queueIndex = sizeLog/o->accessCount() - 1;
1811 if (queueIndex < 0)
1812 queueIndex = 0;
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) {
1828 return;
1831 object->m_next = 0;
1832 object->m_prev = 0;
1834 if (next)
1835 next->m_prev = prev;
1836 else if (list->m_tail == object)
1837 list->m_tail = prev;
1839 if (prev)
1840 prev->m_next = next;
1841 else if (head == object)
1842 head = next;
1844 totalSizeOfLRU -= object->size();
1847 void Cache::insertInLRUList(CachedObject *object)
1849 removeFromLRUList(object);
1851 assert( 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;
1861 if (head)
1862 head->m_prev = object;
1863 head = 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*/) {}
1878 #undef CDEBUG
1880 #include "loader.moc"