KHtml

khtml_pagecache.cpp
1 /* This file is part of the KDE project
2  *
3  * Copyright (C) 2000 Waldo Bastian <[email protected]>
4  * Copyright (C) 2007 Nick Shaforostoff <[email protected]>
5  *
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.
10  *
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.
15  *
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.
20  */
21 
22 #include "khtml_pagecache.h"
23 
24 #include <kcompressiondevice.h>
25 #include <QTemporaryFile>
26 
27 #include <QQueue>
28 #include <QHash>
29 #include <QList>
30 #include <QTimer>
31 #include <QFile>
32 #include <QDir>
33 #include <QDataStream>
34 #include <assert.h>
35 
36 // We keep 12 pages in memory.
37 #ifndef KHTML_PAGE_CACHE_SIZE
38 #define KHTML_PAGE_CACHE_SIZE 12
39 #endif
40 
41 template class QList<KHTMLPageCacheDelivery *>;
42 class KHTMLPageCacheEntry
43 {
44  friend class KHTMLPageCache;
45 public:
46  KHTMLPageCacheEntry(long id);
47 
48  ~KHTMLPageCacheEntry();
49 
50  void addData(const QByteArray &data);
51  void endData();
52 
53  bool isComplete() const
54  {
55  return m_complete;
56  }
57  QString fileName() const
58  {
59  return m_fileName;
60  }
61 
62  KHTMLPageCacheDelivery *fetchData(QObject *recvObj, const char *recvSlot);
63 private:
64  long m_id;
65  bool m_complete;
66  QByteArray m_buffer;
67  QIODevice *m_file;
68  QString m_fileName;
69 };
70 
71 class KHTMLPageCachePrivate
72 {
73 public:
74  long newId;
75  bool deliveryActive;
78  QQueue<long> expireQueue;
79 };
80 
81 KHTMLPageCacheEntry::KHTMLPageCacheEntry(long id)
82  : m_id(id)
83  , m_complete(false)
84 {
85  QTemporaryFile f(QDir::tempPath() + "/khtmlcacheXXXXXX.tmp");
86  f.open();
87  m_fileName = f.fileName();
88  f.setAutoRemove(false);
89 
90  m_file = new KCompressionDevice(m_fileName, KCompressionDevice::GZip);
91  m_file->open(QIODevice::WriteOnly);
92 }
93 
94 KHTMLPageCacheEntry::~KHTMLPageCacheEntry()
95 {
96  delete m_file;
97  QFile::remove(m_fileName);
98 }
99 
100 void
101 KHTMLPageCacheEntry::addData(const QByteArray &data)
102 {
103  m_buffer += data;
104 }
105 
106 void
107 KHTMLPageCacheEntry::endData()
108 {
109  m_complete = true;
110  m_file->write(m_buffer);
111  m_buffer.clear();
112  m_file->close();
113 }
114 
115 KHTMLPageCacheDelivery *
116 KHTMLPageCacheEntry::fetchData(QObject *recvObj, const char *recvSlot)
117 {
118  // Duplicate fd so that entry can be safely deleted while delivering the data.
119  KCompressionDevice *dev = new KCompressionDevice(m_fileName, KCompressionDevice::GZip);
121  KHTMLPageCacheDelivery *delivery = new KHTMLPageCacheDelivery(dev); // takes ownership of dev
122 
123  recvObj->connect(delivery, SIGNAL(emitData(QByteArray)), recvSlot);
124  delivery->recvObj = recvObj;
125  return delivery;
126 }
127 
128 class KHTMLPageCacheSingleton
129 {
130 public:
131  KHTMLPageCache instance;
132 };
133 
134 Q_GLOBAL_STATIC(KHTMLPageCacheSingleton, _self)
135 
138 {
139  return &_self()->instance;
140 }
141 
142 KHTMLPageCache::KHTMLPageCache()
143  : d(new KHTMLPageCachePrivate)
144 {
145  d->newId = 1;
146  d->deliveryActive = false;
147 }
148 
149 KHTMLPageCache::~KHTMLPageCache()
150 {
151  qDeleteAll(d->dict);
152  qDeleteAll(d->delivery);
153  delete d;
154 }
155 
156 long
158 {
159 
160  KHTMLPageCacheEntry *entry = new KHTMLPageCacheEntry(d->newId);
161  d->dict.insert(d->newId, entry);
162  d->expireQueue.append(d->newId);
163  if (d->expireQueue.count() > KHTML_PAGE_CACHE_SIZE) {
164  delete d->dict.take(d->expireQueue.dequeue());
165  }
166  return (d->newId++);
167 }
168 
169 void
170 KHTMLPageCache::addData(long id, const QByteArray &data)
171 {
172 
173  KHTMLPageCacheEntry *entry = d->dict.value(id);
174  if (entry) {
175  entry->addData(data);
176  }
177 }
178 
179 void
181 {
182  KHTMLPageCacheEntry *entry = d->dict.value(id);
183  if (entry) {
184  entry->endData();
185  }
186 }
187 
188 void
190 {
191  KHTMLPageCacheEntry *entry = d->dict.take(id);
192  if (entry) {
193  d->expireQueue.removeAll(entry->m_id);
194  delete entry;
195  }
196 }
197 
198 bool
200 {
201  return d->dict.contains(id);
202 }
203 
204 bool
206 {
207  KHTMLPageCacheEntry *entry = d->dict.value(id);
208  if (entry) {
209  return entry->isComplete();
210  }
211  return false;
212 }
213 
214 void
215 KHTMLPageCache::fetchData(long id, QObject *recvObj, const char *recvSlot)
216 {
217  KHTMLPageCacheEntry *entry = d->dict.value(id);
218  if (!entry || !entry->isComplete()) {
219  return;
220  }
221 
222  // Make this entry the most recent entry.
223  d->expireQueue.removeAll(entry->m_id);
224  d->expireQueue.enqueue(entry->m_id);
225 
226  d->delivery.append(entry->fetchData(recvObj, recvSlot));
227  if (!d->deliveryActive) {
228  d->deliveryActive = true;
229  QTimer::singleShot(20, this, SLOT(sendData()));
230  }
231 }
232 
233 void
235 {
237  while (it.hasNext()) {
238  KHTMLPageCacheDelivery *delivery = it.next();
239  if (delivery->recvObj == recvObj) {
240  delete delivery;
241  it.remove();
242  }
243  }
244 }
245 
246 void
247 KHTMLPageCache::sendData()
248 {
249  if (d->delivery.isEmpty()) {
250  d->deliveryActive = false;
251  return;
252  }
253 
254  KHTMLPageCacheDelivery *delivery = d->delivery.takeFirst();
255  assert(delivery);
256 
257  QByteArray byteArray(delivery->file->read(64 * 1024));
258  delivery->emitData(byteArray);
259 
260  //put back in queue
261  if (delivery->file->atEnd()) {
262  // done.
263  delivery->file->close();
264  delivery->emitData(QByteArray()); // Empty array
265  delete delivery;
266  } else {
267  d->delivery.append(delivery);
268  }
269 
270  QTimer::singleShot(0, this, SLOT(sendData()));
271 }
272 
273 void
275 {
276  assert(d->dict.contains(id));
277  KHTMLPageCacheEntry *entry = d->dict.value(id);
278 
279  if (!entry->isComplete()) {
280  QTimer::singleShot(20, this, SLOT(saveData()));
281  return;
282  }
283 
284  KCompressionDevice file(entry->fileName(), KCompressionDevice::GZip);
285  if (!file.open(QIODevice::ReadOnly)) {
286  return;
287  }
288 
289  const QByteArray byteArray(file.readAll());
290  file.close();
291 
292  str->writeRawData(byteArray.constData(), byteArray.length());
293 
294 }
295 
296 KHTMLPageCacheDelivery::~KHTMLPageCacheDelivery()
297 {
298  file->close();
299  delete file;
300 }
301 
void fetchData(long id, QObject *recvObj, const char *recvSlot)
Fetch data for cache entry id and send it to slot recvSlot in the object recvObj. ...
void clear()
bool remove()
bool isComplete(long id)
bool isValid(long id)
QString tempPath()
void cancelFetch(QObject *recvObj)
Cancel sending data to recvObj.
void cancelEntry(long id)
Cancel the entry.
bool hasNext() const const
Singleton Object that handles a binary cache on top of the http cache management of kio...
long createCacheEntry()
Create a new cache entry.
int writeRawData(const char *s, int len)
bool open(QIODevice::OpenMode mode) override
void saveData(long id, QDataStream *str)
Save the data of cache entry id to the datastream str.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void addData(long id, const QByteArray &data)
Add data to the cache entry with id id.
void endData(long id)
Signal end of data for the cache entry with id id.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Aug 12 2020 22:46:19 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.