/* This file is part of the KDE project * * Copyright (C) 2000 Waldo Bastian * Copyright (C) 2007 Nick Shaforostoff * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "khtml_pagecache.h" #include #include #include #include #include #include #include #include #include #include #include #include // We keep 12 pages in memory. #ifndef KHTML_PAGE_CACHE_SIZE #define KHTML_PAGE_CACHE_SIZE 12 #endif template class QList; class KHTMLPageCacheEntry { friend class KHTMLPageCache; public: KHTMLPageCacheEntry(long id); ~KHTMLPageCacheEntry(); void addData(const QByteArray &data); void endData(); bool isComplete() const {return m_complete;} QString fileName() const {return m_fileName;} KHTMLPageCacheDelivery *fetchData(QObject *recvObj, const char *recvSlot); private: long m_id; bool m_complete; QByteArray m_buffer; QIODevice* m_file; QString m_fileName; }; class KHTMLPageCachePrivate { public: long newId; bool deliveryActive; QHash dict; QList delivery; QQueue expireQueue; }; KHTMLPageCacheEntry::KHTMLPageCacheEntry(long id) : m_id(id) , m_complete(false) { //get tmp file name QTemporaryFile* f=new QTemporaryFile(KStandardDirs::locateLocal("tmp", "")+"khtmlcacheXXXXXX.tmp"); f->open(); m_fileName=f->fileName(); f->setAutoRemove(false); delete f; m_file = KFilterDev::deviceForFile(m_fileName, "application/x-gzip"/*,false*/); m_file->open(QIODevice::WriteOnly); } KHTMLPageCacheEntry::~KHTMLPageCacheEntry() { delete m_file; QFile::remove(m_fileName); } void KHTMLPageCacheEntry::addData(const QByteArray &data) { m_buffer+=data; } void KHTMLPageCacheEntry::endData() { m_complete = true; m_file->write(m_buffer); m_buffer.clear(); m_file->close(); } KHTMLPageCacheDelivery * KHTMLPageCacheEntry::fetchData(QObject *recvObj, const char *recvSlot) { // Duplicate fd so that entry can be safely deleted while delivering the data. KHTMLPageCacheDelivery *delivery=new KHTMLPageCacheDelivery( KFilterDev::deviceForFile (m_fileName, "application/x-gzip") ); delivery->file->open(QIODevice::ReadOnly); recvObj->connect(delivery, SIGNAL(emitData(QByteArray)), recvSlot); delivery->recvObj = recvObj; return delivery; } KHTMLPageCache * KHTMLPageCache::self() { K_GLOBAL_STATIC(KHTMLPageCache, _self) return _self; } KHTMLPageCache::KHTMLPageCache() :d( new KHTMLPageCachePrivate) { d->newId = 1; d->deliveryActive = false; } KHTMLPageCache::~KHTMLPageCache() { qDeleteAll(d->dict); qDeleteAll(d->delivery); delete d; } long KHTMLPageCache::createCacheEntry() { KHTMLPageCacheEntry *entry = new KHTMLPageCacheEntry(d->newId); d->dict.insert(d->newId, entry); d->expireQueue.append(d->newId); if (d->expireQueue.count() > KHTML_PAGE_CACHE_SIZE) delete d->dict.take(d->expireQueue.dequeue()); return (d->newId++); } void KHTMLPageCache::addData(long id, const QByteArray &data) { KHTMLPageCacheEntry *entry = d->dict.value( id ); if (entry) entry->addData(data); } void KHTMLPageCache::endData(long id) { KHTMLPageCacheEntry *entry = d->dict.value( id ); if (entry) entry->endData(); } void KHTMLPageCache::cancelEntry(long id) { KHTMLPageCacheEntry *entry = d->dict.take( id ); if (entry) { d->expireQueue.removeAll(entry->m_id); delete entry; } } bool KHTMLPageCache::isValid(long id) { return d->dict.contains(id); } bool KHTMLPageCache::isComplete(long id) { KHTMLPageCacheEntry *entry = d->dict.value( id ); if (entry) return entry->isComplete(); return false; } void KHTMLPageCache::fetchData(long id, QObject *recvObj, const char *recvSlot) { KHTMLPageCacheEntry *entry = d->dict.value( id ); if (!entry || !entry->isComplete()) return; // Make this entry the most recent entry. d->expireQueue.removeAll(entry->m_id); d->expireQueue.enqueue(entry->m_id); d->delivery.append( entry->fetchData(recvObj, recvSlot) ); if (!d->deliveryActive) { d->deliveryActive = true; QTimer::singleShot(20, this, SLOT(sendData())); } } void KHTMLPageCache::cancelFetch(QObject *recvObj) { QMutableListIterator it( d->delivery ); while (it.hasNext()) { KHTMLPageCacheDelivery* delivery = it.next(); if (delivery->recvObj == recvObj) { delete delivery; it.remove(); } } } void KHTMLPageCache::sendData() { if (d->delivery.isEmpty()) { d->deliveryActive = false; return; } KHTMLPageCacheDelivery *delivery = d->delivery.takeFirst(); assert(delivery); QByteArray byteArray(delivery->file->read(64*1024)); delivery->emitData(byteArray); //put back in queue if (delivery->file->atEnd()) { // done. delivery->file->close(); delivery->emitData(QByteArray()); // Empty array delete delivery; } else d->delivery.append( delivery ); QTimer::singleShot(0, this, SLOT(sendData())); } void KHTMLPageCache::saveData(long id, QDataStream *str) { assert(d->dict.contains( id )); KHTMLPageCacheEntry *entry = d->dict.value( id ); if (!entry->isComplete()) { QTimer::singleShot(20, this, SLOT(saveData())); return; } QIODevice* file = KFilterDev::deviceForFile (entry->fileName(), "application/x-gzip"); if (!file->open(QIODevice::ReadOnly)) return; QByteArray byteArray(file->readAll()); file->close(); str->writeRawData(byteArray.constData(), byteArray.length()); } KHTMLPageCacheDelivery::~KHTMLPageCacheDelivery() { file->close(); delete file; } #include "moc_khtml_pagecache.cpp"