File indexing completed on 2024-04-14 14:23:11

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 
0302 #include "moc_khtml_pagecache.cpp"