KHTML
khtml_pagecache.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 * 00003 * Copyright (C) 2000 Waldo Bastian <bastian@kde.org> 00004 * Copyright (C) 2007 Nick Shaforostoff <shafff@ukr.net> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Library General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Library General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Library General Public License 00017 * along with this library; see the file COPYING.LIB. If not, write to 00018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 * Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "khtml_pagecache.h" 00023 00024 #include <kfilterdev.h> 00025 #include <QTemporaryFile> 00026 #include <kstandarddirs.h> 00027 00028 #include <QQueue> 00029 #include <QHash> 00030 #include <QList> 00031 #include <QtCore/QTimer> 00032 #include <QtCore/QFile> 00033 #include <errno.h> 00034 #include <sys/types.h> 00035 #include <unistd.h> 00036 #include <assert.h> 00037 00038 // We keep 12 pages in memory. 00039 #ifndef KHTML_PAGE_CACHE_SIZE 00040 #define KHTML_PAGE_CACHE_SIZE 12 00041 #endif 00042 00043 template class QList<KHTMLPageCacheDelivery*>; 00044 class KHTMLPageCacheEntry 00045 { 00046 friend class KHTMLPageCache; 00047 public: 00048 KHTMLPageCacheEntry(long id); 00049 00050 ~KHTMLPageCacheEntry(); 00051 00052 void addData(const QByteArray &data); 00053 void endData(); 00054 00055 bool isComplete() const {return m_complete;} 00056 QString fileName() const {return m_fileName;} 00057 00058 KHTMLPageCacheDelivery *fetchData(QObject *recvObj, const char *recvSlot); 00059 private: 00060 long m_id; 00061 bool m_complete; 00062 QByteArray m_buffer; 00063 QIODevice* m_file; 00064 QString m_fileName; 00065 }; 00066 00067 class KHTMLPageCachePrivate 00068 { 00069 public: 00070 long newId; 00071 bool deliveryActive; 00072 QHash<int, KHTMLPageCacheEntry*> dict; 00073 QList<KHTMLPageCacheDelivery*> delivery; 00074 QQueue<long> expireQueue; 00075 }; 00076 00077 KHTMLPageCacheEntry::KHTMLPageCacheEntry(long id) 00078 : m_id(id) 00079 , m_complete(false) 00080 { 00081 //get tmp file name 00082 QTemporaryFile* f=new QTemporaryFile(KStandardDirs::locateLocal("tmp", "")+"khtmlcacheXXXXXX.tmp"); 00083 f->open(); 00084 m_fileName=f->fileName(); 00085 f->setAutoRemove(false); 00086 delete f; 00087 00088 m_file = KFilterDev::deviceForFile(m_fileName, "application/x-gzip"/*,false*/); 00089 m_file->open(QIODevice::WriteOnly); 00090 } 00091 00092 KHTMLPageCacheEntry::~KHTMLPageCacheEntry() 00093 { 00094 delete m_file; 00095 QFile::remove(m_fileName); 00096 } 00097 00098 00099 void 00100 KHTMLPageCacheEntry::addData(const QByteArray &data) 00101 { 00102 m_buffer+=data; 00103 } 00104 00105 void 00106 KHTMLPageCacheEntry::endData() 00107 { 00108 m_complete = true; 00109 m_file->write(m_buffer); 00110 m_buffer.clear(); 00111 m_file->close(); 00112 } 00113 00114 00115 KHTMLPageCacheDelivery * 00116 KHTMLPageCacheEntry::fetchData(QObject *recvObj, const char *recvSlot) 00117 { 00118 // Duplicate fd so that entry can be safely deleted while delivering the data. 00119 KHTMLPageCacheDelivery *delivery=new KHTMLPageCacheDelivery( 00120 KFilterDev::deviceForFile (m_fileName, "application/x-gzip") 00121 ); 00122 delivery->file->open(QIODevice::ReadOnly); 00123 00124 recvObj->connect(delivery, SIGNAL(emitData(const QByteArray&)), recvSlot); 00125 delivery->recvObj = recvObj; 00126 return delivery; 00127 } 00128 00129 KHTMLPageCache * 00130 KHTMLPageCache::self() 00131 { 00132 K_GLOBAL_STATIC(KHTMLPageCache, _self) 00133 return _self; 00134 } 00135 00136 KHTMLPageCache::KHTMLPageCache() 00137 :d( new KHTMLPageCachePrivate) 00138 { 00139 d->newId = 1; 00140 d->deliveryActive = false; 00141 } 00142 00143 KHTMLPageCache::~KHTMLPageCache() 00144 { 00145 qDeleteAll(d->dict); 00146 qDeleteAll(d->delivery); 00147 delete d; 00148 } 00149 00150 long 00151 KHTMLPageCache::createCacheEntry() 00152 { 00153 00154 KHTMLPageCacheEntry *entry = new KHTMLPageCacheEntry(d->newId); 00155 d->dict.insert(d->newId, entry); 00156 d->expireQueue.append(d->newId); 00157 if (d->expireQueue.count() > KHTML_PAGE_CACHE_SIZE) 00158 delete d->dict.take(d->expireQueue.dequeue()); 00159 return (d->newId++); 00160 } 00161 00162 void 00163 KHTMLPageCache::addData(long id, const QByteArray &data) 00164 { 00165 00166 KHTMLPageCacheEntry *entry = d->dict.value( id ); 00167 if (entry) 00168 entry->addData(data); 00169 } 00170 00171 void 00172 KHTMLPageCache::endData(long id) 00173 { 00174 KHTMLPageCacheEntry *entry = d->dict.value( id ); 00175 if (entry) 00176 entry->endData(); 00177 } 00178 00179 void 00180 KHTMLPageCache::cancelEntry(long id) 00181 { 00182 KHTMLPageCacheEntry *entry = d->dict.take( id ); 00183 if (entry) 00184 { 00185 d->expireQueue.removeAll(entry->m_id); 00186 delete entry; 00187 } 00188 } 00189 00190 bool 00191 KHTMLPageCache::isValid(long id) 00192 { 00193 return d->dict.contains(id); 00194 } 00195 00196 bool 00197 KHTMLPageCache::isComplete(long id) 00198 { 00199 KHTMLPageCacheEntry *entry = d->dict.value( id ); 00200 if (entry) 00201 return entry->isComplete(); 00202 return false; 00203 } 00204 00205 void 00206 KHTMLPageCache::fetchData(long id, QObject *recvObj, const char *recvSlot) 00207 { 00208 KHTMLPageCacheEntry *entry = d->dict.value( id ); 00209 if (!entry || !entry->isComplete()) return; 00210 00211 // Make this entry the most recent entry. 00212 d->expireQueue.removeAll(entry->m_id); 00213 d->expireQueue.enqueue(entry->m_id); 00214 00215 d->delivery.append( entry->fetchData(recvObj, recvSlot) ); 00216 if (!d->deliveryActive) 00217 { 00218 d->deliveryActive = true; 00219 QTimer::singleShot(20, this, SLOT(sendData())); 00220 } 00221 } 00222 00223 void 00224 KHTMLPageCache::cancelFetch(QObject *recvObj) 00225 { 00226 QMutableListIterator<KHTMLPageCacheDelivery*> it( d->delivery ); 00227 while (it.hasNext()) { 00228 KHTMLPageCacheDelivery* delivery = it.next(); 00229 if (delivery->recvObj == recvObj) 00230 { 00231 delete delivery; 00232 it.remove(); 00233 } 00234 } 00235 } 00236 00237 void 00238 KHTMLPageCache::sendData() 00239 { 00240 if (d->delivery.isEmpty()) 00241 { 00242 d->deliveryActive = false; 00243 return; 00244 } 00245 00246 KHTMLPageCacheDelivery *delivery = d->delivery.takeFirst(); 00247 assert(delivery); 00248 00249 QByteArray byteArray(delivery->file->read(64*1024)); 00250 delivery->emitData(byteArray); 00251 00252 //put back in queue 00253 if (delivery->file->atEnd()) 00254 { 00255 // done. 00256 delivery->file->close(); 00257 delivery->emitData(QByteArray()); // Empty array 00258 delete delivery; 00259 } 00260 else 00261 d->delivery.append( delivery ); 00262 00263 QTimer::singleShot(0, this, SLOT(sendData())); 00264 } 00265 00266 void 00267 KHTMLPageCache::saveData(long id, QDataStream *str) 00268 { 00269 assert(d->dict.contains( id )); 00270 KHTMLPageCacheEntry *entry = d->dict.value( id ); 00271 00272 if (!entry->isComplete()) 00273 { 00274 QTimer::singleShot(20, this, SLOT(saveData())); 00275 return; 00276 } 00277 00278 QIODevice* file = KFilterDev::deviceForFile (entry->fileName(), "application/x-gzip"); 00279 if (!file->open(QIODevice::ReadOnly)) 00280 return; 00281 00282 QByteArray byteArray(file->readAll()); 00283 file->close(); 00284 00285 str->writeRawData(byteArray.constData(), byteArray.length()); 00286 00287 } 00288 00289 KHTMLPageCacheDelivery::~KHTMLPageCacheDelivery() 00290 { 00291 file->close(); 00292 delete file; 00293 } 00294 00295 #include "khtml_pagecache.moc"
KDE 4.6 API Reference