File indexing completed on 2023-05-30 09:09:52
0001 /* This file is part of the KDE project 0002 * 0003 * Copyright (C) 2000 Waldo Bastian <bastian@kde.org> 0004 * Copyright (C) 2007 Nick Shaforostoff <shafff@ukr.net> 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Library General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Library General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Library General Public License 0017 * along with this library; see the file COPYING.LIB. If not, write to 0018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 * Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "khtml_pagecache.h" 0023 0024 #include <kcompressiondevice.h> 0025 #include <QTemporaryFile> 0026 0027 #include <QQueue> 0028 #include <QHash> 0029 #include <QList> 0030 #include <QTimer> 0031 #include <QFile> 0032 #include <QDir> 0033 #include <QDataStream> 0034 #include <assert.h> 0035 0036 // We keep 12 pages in memory. 0037 #ifndef KHTML_PAGE_CACHE_SIZE 0038 #define KHTML_PAGE_CACHE_SIZE 12 0039 #endif 0040 0041 template class QList<KHTMLPageCacheDelivery *>; 0042 class KHTMLPageCacheEntry 0043 { 0044 friend class KHTMLPageCache; 0045 public: 0046 KHTMLPageCacheEntry(long id); 0047 0048 ~KHTMLPageCacheEntry(); 0049 0050 void addData(const QByteArray &data); 0051 void endData(); 0052 0053 bool isComplete() const 0054 { 0055 return m_complete; 0056 } 0057 QString fileName() const 0058 { 0059 return m_fileName; 0060 } 0061 0062 KHTMLPageCacheDelivery *fetchData(QObject *recvObj, const char *recvSlot); 0063 private: 0064 long m_id; 0065 bool m_complete; 0066 QByteArray m_buffer; 0067 QIODevice *m_file; 0068 QString m_fileName; 0069 }; 0070 0071 class KHTMLPageCachePrivate 0072 { 0073 public: 0074 long newId; 0075 bool deliveryActive; 0076 QHash<int, KHTMLPageCacheEntry *> dict; 0077 QList<KHTMLPageCacheDelivery *> delivery; 0078 QQueue<long> expireQueue; 0079 }; 0080 0081 KHTMLPageCacheEntry::KHTMLPageCacheEntry(long id) 0082 : m_id(id) 0083 , m_complete(false) 0084 { 0085 QTemporaryFile f(QDir::tempPath() + "/khtmlcacheXXXXXX.tmp"); 0086 f.open(); 0087 m_fileName = f.fileName(); 0088 f.setAutoRemove(false); 0089 0090 m_file = new KCompressionDevice(m_fileName, KCompressionDevice::GZip); 0091 m_file->open(QIODevice::WriteOnly); 0092 } 0093 0094 KHTMLPageCacheEntry::~KHTMLPageCacheEntry() 0095 { 0096 delete m_file; 0097 QFile::remove(m_fileName); 0098 } 0099 0100 void 0101 KHTMLPageCacheEntry::addData(const QByteArray &data) 0102 { 0103 m_buffer += data; 0104 } 0105 0106 void 0107 KHTMLPageCacheEntry::endData() 0108 { 0109 m_complete = true; 0110 m_file->write(m_buffer); 0111 m_buffer.clear(); 0112 m_file->close(); 0113 } 0114 0115 KHTMLPageCacheDelivery * 0116 KHTMLPageCacheEntry::fetchData(QObject *recvObj, const char *recvSlot) 0117 { 0118 // Duplicate fd so that entry can be safely deleted while delivering the data. 0119 KCompressionDevice *dev = new KCompressionDevice(m_fileName, KCompressionDevice::GZip); 0120 dev->open(QIODevice::ReadOnly); 0121 KHTMLPageCacheDelivery *delivery = new KHTMLPageCacheDelivery(dev); // takes ownership of dev 0122 0123 recvObj->connect(delivery, SIGNAL(emitData(QByteArray)), recvSlot); 0124 delivery->recvObj = recvObj; 0125 return delivery; 0126 } 0127 0128 class KHTMLPageCacheSingleton 0129 { 0130 public: 0131 KHTMLPageCache instance; 0132 }; 0133 0134 Q_GLOBAL_STATIC(KHTMLPageCacheSingleton, _self) 0135 0136 KHTMLPageCache * 0137 KHTMLPageCache::self() 0138 { 0139 return &_self()->instance; 0140 } 0141 0142 KHTMLPageCache::KHTMLPageCache() 0143 : d(new KHTMLPageCachePrivate) 0144 { 0145 d->newId = 1; 0146 d->deliveryActive = false; 0147 } 0148 0149 KHTMLPageCache::~KHTMLPageCache() 0150 { 0151 qDeleteAll(d->dict); 0152 qDeleteAll(d->delivery); 0153 delete d; 0154 } 0155 0156 long 0157 KHTMLPageCache::createCacheEntry() 0158 { 0159 0160 KHTMLPageCacheEntry *entry = new KHTMLPageCacheEntry(d->newId); 0161 d->dict.insert(d->newId, entry); 0162 d->expireQueue.append(d->newId); 0163 if (d->expireQueue.count() > KHTML_PAGE_CACHE_SIZE) { 0164 delete d->dict.take(d->expireQueue.dequeue()); 0165 } 0166 return (d->newId++); 0167 } 0168 0169 void 0170 KHTMLPageCache::addData(long id, const QByteArray &data) 0171 { 0172 0173 KHTMLPageCacheEntry *entry = d->dict.value(id); 0174 if (entry) { 0175 entry->addData(data); 0176 } 0177 } 0178 0179 void 0180 KHTMLPageCache::endData(long id) 0181 { 0182 KHTMLPageCacheEntry *entry = d->dict.value(id); 0183 if (entry) { 0184 entry->endData(); 0185 } 0186 } 0187 0188 void 0189 KHTMLPageCache::cancelEntry(long id) 0190 { 0191 KHTMLPageCacheEntry *entry = d->dict.take(id); 0192 if (entry) { 0193 d->expireQueue.removeAll(entry->m_id); 0194 delete entry; 0195 } 0196 } 0197 0198 bool 0199 KHTMLPageCache::isValid(long id) 0200 { 0201 return d->dict.contains(id); 0202 } 0203 0204 bool 0205 KHTMLPageCache::isComplete(long id) 0206 { 0207 KHTMLPageCacheEntry *entry = d->dict.value(id); 0208 if (entry) { 0209 return entry->isComplete(); 0210 } 0211 return false; 0212 } 0213 0214 void 0215 KHTMLPageCache::fetchData(long id, QObject *recvObj, const char *recvSlot) 0216 { 0217 KHTMLPageCacheEntry *entry = d->dict.value(id); 0218 if (!entry || !entry->isComplete()) { 0219 return; 0220 } 0221 0222 // Make this entry the most recent entry. 0223 d->expireQueue.removeAll(entry->m_id); 0224 d->expireQueue.enqueue(entry->m_id); 0225 0226 d->delivery.append(entry->fetchData(recvObj, recvSlot)); 0227 if (!d->deliveryActive) { 0228 d->deliveryActive = true; 0229 QTimer::singleShot(20, this, SLOT(sendData())); 0230 } 0231 } 0232 0233 void 0234 KHTMLPageCache::cancelFetch(QObject *recvObj) 0235 { 0236 QMutableListIterator<KHTMLPageCacheDelivery *> it(d->delivery); 0237 while (it.hasNext()) { 0238 KHTMLPageCacheDelivery *delivery = it.next(); 0239 if (delivery->recvObj == recvObj) { 0240 delete delivery; 0241 it.remove(); 0242 } 0243 } 0244 } 0245 0246 void 0247 KHTMLPageCache::sendData() 0248 { 0249 if (d->delivery.isEmpty()) { 0250 d->deliveryActive = false; 0251 return; 0252 } 0253 0254 KHTMLPageCacheDelivery *delivery = d->delivery.takeFirst(); 0255 assert(delivery); 0256 0257 QByteArray byteArray(delivery->file->read(64 * 1024)); 0258 delivery->emitData(byteArray); 0259 0260 //put back in queue 0261 if (delivery->file->atEnd()) { 0262 // done. 0263 delivery->file->close(); 0264 delivery->emitData(QByteArray()); // Empty array 0265 delete delivery; 0266 } else { 0267 d->delivery.append(delivery); 0268 } 0269 0270 QTimer::singleShot(0, this, SLOT(sendData())); 0271 } 0272 0273 void 0274 KHTMLPageCache::saveData(long id, QDataStream *str) 0275 { 0276 assert(d->dict.contains(id)); 0277 KHTMLPageCacheEntry *entry = d->dict.value(id); 0278 0279 if (!entry->isComplete()) { 0280 QTimer::singleShot(20, this, SLOT(saveData())); 0281 return; 0282 } 0283 0284 KCompressionDevice file(entry->fileName(), KCompressionDevice::GZip); 0285 if (!file.open(QIODevice::ReadOnly)) { 0286 return; 0287 } 0288 0289 const QByteArray byteArray(file.readAll()); 0290 file.close(); 0291 0292 str->writeRawData(byteArray.constData(), byteArray.length()); 0293 0294 } 0295 0296 KHTMLPageCacheDelivery::~KHTMLPageCacheDelivery() 0297 { 0298 file->close(); 0299 delete file; 0300 } 0301