fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / khtml / khtml_pagecache.cpp
blobf3b37e0e38c884d6d1e3a5b455f273a180db7a7d
1 /* This file is part of the KDE project
3 * Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
4 * Copyright (C) 2007 Nick Shaforostoff <shafff@ukr.net>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
22 #include "khtml_pagecache.h"
24 #include <kfilterdev.h>
25 #include <QTemporaryFile>
26 #include <kstandarddirs.h>
28 #include <QQueue>
29 #include <QHash>
30 #include <QList>
31 #include <QtCore/QTimer>
32 #include <QtCore/QFile>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <assert.h>
38 // We keep 12 pages in memory.
39 #ifndef KHTML_PAGE_CACHE_SIZE
40 #define KHTML_PAGE_CACHE_SIZE 12
41 #endif
43 template class QList<KHTMLPageCacheDelivery*>;
44 class KHTMLPageCacheEntry
46 friend class KHTMLPageCache;
47 public:
48 KHTMLPageCacheEntry(long id);
50 ~KHTMLPageCacheEntry();
52 void addData(const QByteArray &data);
53 void endData();
55 bool isComplete() const {return m_complete;}
56 QString fileName() const {return m_fileName;}
58 KHTMLPageCacheDelivery *fetchData(QObject *recvObj, const char *recvSlot);
59 private:
60 long m_id;
61 bool m_complete;
62 QByteArray m_buffer;
63 QIODevice* m_file;
64 QString m_fileName;
67 class KHTMLPageCachePrivate
69 public:
70 long newId;
71 bool deliveryActive;
72 QHash<int, KHTMLPageCacheEntry*> dict;
73 QList<KHTMLPageCacheDelivery*> delivery;
74 QQueue<long> expireQueue;
77 KHTMLPageCacheEntry::KHTMLPageCacheEntry(long id)
78 : m_id(id)
79 , m_complete(false)
81 //get tmp file name
82 QTemporaryFile* f=new QTemporaryFile(KStandardDirs::locateLocal("tmp", "")+"khtmlcacheXXXXXX.tmp");
83 f->open();
84 m_fileName=f->fileName();
85 f->setAutoRemove(false);
86 delete f;
88 m_file = KFilterDev::deviceForFile(m_fileName, "application/x-gzip"/*,false*/);
89 m_file->open(QIODevice::WriteOnly);
92 KHTMLPageCacheEntry::~KHTMLPageCacheEntry()
94 delete m_file;
95 QFile::remove(m_fileName);
99 void
100 KHTMLPageCacheEntry::addData(const QByteArray &data)
102 m_buffer+=data;
105 void
106 KHTMLPageCacheEntry::endData()
108 m_complete = true;
109 m_file->write(m_buffer);
110 m_buffer.clear();
111 m_file->close();
115 KHTMLPageCacheDelivery *
116 KHTMLPageCacheEntry::fetchData(QObject *recvObj, const char *recvSlot)
118 // Duplicate fd so that entry can be safely deleted while delivering the data.
119 KHTMLPageCacheDelivery *delivery=new KHTMLPageCacheDelivery(
120 KFilterDev::deviceForFile (m_fileName, "application/x-gzip")
122 delivery->file->open(QIODevice::ReadOnly);
124 recvObj->connect(delivery, SIGNAL(emitData(const QByteArray&)), recvSlot);
125 delivery->recvObj = recvObj;
126 return delivery;
129 KHTMLPageCache *
130 KHTMLPageCache::self()
132 K_GLOBAL_STATIC(KHTMLPageCache, _self)
133 return _self;
136 KHTMLPageCache::KHTMLPageCache()
137 :d( new KHTMLPageCachePrivate)
139 d->newId = 1;
140 d->deliveryActive = false;
143 KHTMLPageCache::~KHTMLPageCache()
145 qDeleteAll(d->dict);
146 qDeleteAll(d->delivery);
147 delete d;
150 long
151 KHTMLPageCache::createCacheEntry()
154 KHTMLPageCacheEntry *entry = new KHTMLPageCacheEntry(d->newId);
155 d->dict.insert(d->newId, entry);
156 d->expireQueue.append(d->newId);
157 if (d->expireQueue.count() > KHTML_PAGE_CACHE_SIZE)
158 delete d->dict.take(d->expireQueue.dequeue());
159 return (d->newId++);
162 void
163 KHTMLPageCache::addData(long id, const QByteArray &data)
166 KHTMLPageCacheEntry *entry = d->dict.value( id );
167 if (entry)
168 entry->addData(data);
171 void
172 KHTMLPageCache::endData(long id)
174 KHTMLPageCacheEntry *entry = d->dict.value( id );
175 if (entry)
176 entry->endData();
179 void
180 KHTMLPageCache::cancelEntry(long id)
182 KHTMLPageCacheEntry *entry = d->dict.take( id );
183 if (entry)
185 d->expireQueue.removeAll(entry->m_id);
186 delete entry;
190 bool
191 KHTMLPageCache::isValid(long id)
193 return d->dict.contains(id);
196 bool
197 KHTMLPageCache::isComplete(long id)
199 KHTMLPageCacheEntry *entry = d->dict.value( id );
200 if (entry)
201 return entry->isComplete();
202 return false;
205 void
206 KHTMLPageCache::fetchData(long id, QObject *recvObj, const char *recvSlot)
208 KHTMLPageCacheEntry *entry = d->dict.value( id );
209 if (!entry || !entry->isComplete()) return;
211 // Make this entry the most recent entry.
212 d->expireQueue.removeAll(entry->m_id);
213 d->expireQueue.enqueue(entry->m_id);
215 d->delivery.append( entry->fetchData(recvObj, recvSlot) );
216 if (!d->deliveryActive)
218 d->deliveryActive = true;
219 QTimer::singleShot(20, this, SLOT(sendData()));
223 void
224 KHTMLPageCache::cancelFetch(QObject *recvObj)
226 QMutableListIterator<KHTMLPageCacheDelivery*> it( d->delivery );
227 while (it.hasNext()) {
228 KHTMLPageCacheDelivery* delivery = it.next();
229 if (delivery->recvObj == recvObj)
231 delete delivery;
232 it.remove();
237 void
238 KHTMLPageCache::sendData()
240 if (d->delivery.isEmpty())
242 d->deliveryActive = false;
243 return;
246 KHTMLPageCacheDelivery *delivery = d->delivery.takeFirst();
247 assert(delivery);
249 QByteArray byteArray(delivery->file->read(64*1024));
250 delivery->emitData(byteArray);
252 //put back in queue
253 if (delivery->file->atEnd())
255 // done.
256 delivery->file->close();
257 delivery->emitData(QByteArray()); // Empty array
258 delete delivery;
260 else
261 d->delivery.append( delivery );
263 QTimer::singleShot(0, this, SLOT(sendData()));
266 void
267 KHTMLPageCache::saveData(long id, QDataStream *str)
269 assert(d->dict.contains( id ));
270 KHTMLPageCacheEntry *entry = d->dict.value( id );
272 if (!entry->isComplete())
274 QTimer::singleShot(20, this, SLOT(saveData()));
275 return;
278 QIODevice* file = KFilterDev::deviceForFile (entry->fileName(), "application/x-gzip");
279 if (!file->open(QIODevice::ReadOnly))
280 return;
282 QByteArray byteArray(file->readAll());
283 file->close();
285 str->writeRawData(byteArray.constData(), byteArray.length());
289 KHTMLPageCacheDelivery::~KHTMLPageCacheDelivery()
291 file->close();
292 delete file;
295 #include "khtml_pagecache.moc"