Merge lp:~osomon/webbrowser-app/limit-thumbnail-cache-size into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Merged
Approved by: Günter Schwann
Approved revision: 286
Merged at revision: 280
Proposed branch: lp:~osomon/webbrowser-app/limit-thumbnail-cache-size
Merge into: lp:webbrowser-app
Diff against target: 275 lines (+117/-22)
7 files modified
src/Ubuntu/Components/Extras/Browser/plugin.cpp (+18/-0)
src/Ubuntu/Components/Extras/Browser/plugin.h (+8/-0)
src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.cpp (+66/-0)
src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.h (+19/-3)
src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.cpp (+6/-17)
tests/unittests/history-domainlist-chronological-model/CMakeLists.txt (+0/-1)
tests/unittests/history-domainlist-model/CMakeLists.txt (+0/-1)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/limit-thumbnail-cache-size
Reviewer Review Type Date Requested Status
Günter Schwann (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+179247@code.launchpad.net

Commit message

Delete old thumbnails to ensure the cache never grows bigger than 5MB.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
283. By Olivier Tilloy

Remove unused dependencies.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Günter Schwann (schwann) wrote :

45 + m_thumbnailUtilsThread->deleteLater();
I'd call m_thumbnailUtilsThread->wait() and delete the thread directly instead

234 + QMetaObject::invokeMethod(&WebThumbnailUtils::instance(), "cacheThumbnail",
You could add the connection type Qt::QueuedConnection, to highlight (and make sure) that it's asynchronous.

148 + qint64 goal = MAX_CACHE_SIZE_IN_BYTES * 9 / 10;
So the max cache size is 90% of 5MB? Why?

284. By Olivier Tilloy

Ensure the thread terminates before deleting it.

285. By Olivier Tilloy

Make it (more) explicit that the method is called asynchronously.

286. By Olivier Tilloy

Do not expire cache entries if the total size doesn’t exceed the limit.

Revision history for this message
Olivier Tilloy (osomon) wrote :

> 45 + m_thumbnailUtilsThread->deleteLater();
> I'd call m_thumbnailUtilsThread->wait() and delete the thread directly instead

Done. Thanks for the suggestion.

> 234 + QMetaObject::invokeMethod(&WebThumbnailUtils::instance(),
> "cacheThumbnail",
> You could add the connection type Qt::QueuedConnection, to highlight (and make
> sure) that it's asynchronous.

Although not strictly necessary, done.

> 148 + qint64 goal = MAX_CACHE_SIZE_IN_BYTES * 9 / 10;
> So the max cache size is 90% of 5MB? Why?

That was a mistake on my part. This code is not triggered only if the total cache size exceeds the limit. The goal is 90% of the limit to have some buffer, so that we do not need to expire entries every time a new thumbnail is generated (this is inspired by the implementation of the QNetworkDiskCache class).

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Günter Schwann (schwann) wrote :

Looks good

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Ubuntu/Components/Extras/Browser/plugin.cpp'
2--- src/Ubuntu/Components/Extras/Browser/plugin.cpp 2013-07-30 17:26:49 +0000
3+++ src/Ubuntu/Components/Extras/Browser/plugin.cpp 2013-08-09 07:47:22 +0000
4@@ -26,11 +26,13 @@
5 #include "tabs-model.h"
6 #include "bookmarks-model.h"
7 #include "webthumbnail-provider.h"
8+#include "webthumbnail-utils.h"
9 #include "webview-thumbnailer.h"
10
11 // Qt
12 #include <QtCore/QDir>
13 #include <QtCore/QStandardPaths>
14+#include <QtCore/QThread>
15 #include <QtQml>
16
17 void UbuntuBrowserPlugin::initializeEngine(QQmlEngine* engine, const char* uri)
18@@ -43,9 +45,18 @@
19 QQmlContext* context = engine->rootContext();
20 context->setContextProperty("dataLocation", dataLocation.absolutePath());
21
22+ // This singleton lives in its own thread to ensure that
23+ // disk I/O is not performed in the UI thread.
24+ WebThumbnailUtils& utils = WebThumbnailUtils::instance();
25+ m_thumbnailUtilsThread = new QThread;
26+ utils.moveToThread(m_thumbnailUtilsThread);
27+ m_thumbnailUtilsThread->start();
28+
29 WebThumbnailProvider* thumbnailer = new WebThumbnailProvider;
30 engine->addImageProvider(QLatin1String("webthumbnail"), thumbnailer);
31 context->setContextProperty("WebThumbnailer", thumbnailer);
32+
33+ connect(engine, SIGNAL(destroyed()), SLOT(onEngineDestroyed()));
34 }
35
36 void UbuntuBrowserPlugin::registerTypes(const char* uri)
37@@ -61,3 +72,10 @@
38 qmlRegisterType<BookmarksModel>(uri, 0, 1, "BookmarksModel");
39 qmlRegisterType<WebviewThumbnailer>(uri, 0, 1, "WebviewThumbnailer");
40 }
41+
42+void UbuntuBrowserPlugin::onEngineDestroyed()
43+{
44+ m_thumbnailUtilsThread->quit();
45+ m_thumbnailUtilsThread->wait();
46+ delete m_thumbnailUtilsThread;
47+}
48
49=== modified file 'src/Ubuntu/Components/Extras/Browser/plugin.h'
50--- src/Ubuntu/Components/Extras/Browser/plugin.h 2013-06-11 12:06:15 +0000
51+++ src/Ubuntu/Components/Extras/Browser/plugin.h 2013-08-09 07:47:22 +0000
52@@ -22,6 +22,8 @@
53 // Qt
54 #include <QtQml/QQmlExtensionPlugin>
55
56+class QThread;
57+
58 class UbuntuBrowserPlugin : public QQmlExtensionPlugin
59 {
60 Q_OBJECT
61@@ -30,6 +32,12 @@
62 public:
63 void initializeEngine(QQmlEngine* engine, const char* uri);
64 void registerTypes(const char* uri);
65+
66+private:
67+ QThread* m_thumbnailUtilsThread;
68+
69+private Q_SLOTS:
70+ void onEngineDestroyed();
71 };
72
73 #endif // __PLUGIN_H__
74
75=== modified file 'src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.cpp'
76--- src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.cpp 2013-07-10 12:02:00 +0000
77+++ src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.cpp 2013-08-09 07:47:22 +0000
78@@ -16,11 +16,32 @@
79 * along with this program. If not, see <http://www.gnu.org/licenses/>.
80 */
81
82+#include "domain-utils.h"
83 #include "webthumbnail-utils.h"
84
85 // Qt
86 #include <QtCore/QCryptographicHash>
87+#include <QtCore/QFile>
88 #include <QtCore/QStandardPaths>
89+#include <QtCore/QUrl>
90+#include <QtGui/QImage>
91+
92+#define MAX_CACHE_SIZE_IN_BYTES 5 * 1024 * 1024 // 5MB
93+
94+WebThumbnailUtils::WebThumbnailUtils(QObject* parent)
95+ : QObject(parent)
96+{
97+}
98+
99+WebThumbnailUtils& WebThumbnailUtils::instance()
100+{
101+ static WebThumbnailUtils utils;
102+ return utils;
103+}
104+
105+WebThumbnailUtils::~WebThumbnailUtils()
106+{
107+}
108
109 QDir WebThumbnailUtils::cacheLocation()
110 {
111@@ -40,3 +61,48 @@
112 QString hash(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Md5).toHex());
113 return cacheLocation().absoluteFilePath(hash + ".png");
114 }
115+
116+void WebThumbnailUtils::cacheThumbnail(const QUrl& url, const QImage& thumbnail) const
117+{
118+ ensureCacheLocation();
119+ QFileInfo file = thumbnailFile(url);
120+ bool saved = thumbnail.save(file.absoluteFilePath());
121+
122+ if (saved) {
123+ // Make a link to the thumbnail file for the corresponding domain’s thumbnail.
124+ QUrl domain(DomainUtils::extractTopLevelDomainName(url));
125+ QString domainThumbnail = WebThumbnailUtils::thumbnailFile(domain).absoluteFilePath();
126+ if (QFile::exists(domainThumbnail)) {
127+ QFile::remove(domainThumbnail);
128+ }
129+ QFile::link(file.fileName(), domainThumbnail);
130+ }
131+
132+ expireCache();
133+}
134+
135+void WebThumbnailUtils::expireCache() const
136+{
137+ QDir cache = cacheLocation();
138+ if (!cache.exists()) {
139+ return;
140+ }
141+ QStringList nameFilters = QStringList() << "*.png";
142+ QDir::Filters filters = QDir::Files | QDir::NoDotAndDotDot;
143+ QDir::SortFlags sort = QDir::Time;
144+ QFileInfoList entries = cache.entryInfoList(nameFilters, filters, sort);
145+ qint64 currentSize = 0;
146+ Q_FOREACH(const QFileInfo& entry, entries) {
147+ currentSize += entry.size();
148+ }
149+ if (currentSize > MAX_CACHE_SIZE_IN_BYTES) {
150+ qint64 goal = MAX_CACHE_SIZE_IN_BYTES * 9 / 10;
151+ while (!entries.isEmpty() && (currentSize > goal)) {
152+ QFileInfo entry = entries.takeLast();
153+ qint64 size = entry.size();
154+ if (QFile::remove(entry.absoluteFilePath())) {
155+ currentSize -= size;
156+ }
157+ }
158+ }
159+}
160
161=== modified file 'src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.h'
162--- src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.h 2013-07-10 12:02:00 +0000
163+++ src/Ubuntu/Components/Extras/Browser/webthumbnail-utils.h 2013-08-09 07:47:22 +0000
164@@ -22,14 +22,30 @@
165 // Qt
166 #include <QtCore/QDir>
167 #include <QtCore/QFileInfo>
168-#include <QtCore/QUrl>
169-
170-class WebThumbnailUtils
171+#include <QtCore/QObject>
172+
173+class QImage;
174+class QUrl;
175+
176+class WebThumbnailUtils : public QObject
177 {
178+ Q_OBJECT
179+
180 public:
181+ static WebThumbnailUtils& instance();
182+ ~WebThumbnailUtils();
183+
184 static QDir cacheLocation();
185 static void ensureCacheLocation();
186 static QFileInfo thumbnailFile(const QUrl& url);
187+
188+public Q_SLOTS:
189+ void cacheThumbnail(const QUrl& url, const QImage& thumbnail) const;
190+
191+private:
192+ WebThumbnailUtils(QObject* parent=0);
193+
194+ void expireCache() const;
195 };
196
197 #endif // __WEBTHUMBNAIL_UTILS_H__
198
199=== modified file 'src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.cpp'
200--- src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.cpp 2013-08-06 10:16:28 +0000
201+++ src/Ubuntu/Components/Extras/Browser/webview-thumbnailer.cpp 2013-08-09 07:47:22 +0000
202@@ -16,12 +16,11 @@
203 * along with this program. If not, see <http://www.gnu.org/licenses/>.
204 */
205
206-#include "domain-utils.h"
207 #include "webthumbnail-utils.h"
208 #include "webview-thumbnailer.h"
209
210 // Qt
211-#include <QtCore/QFile>
212+#include <QtCore/QMetaObject>
213 #include <QtCore/QTimer>
214 #include <QtQuick/private/qsgrenderer_p.h>
215 #include <QtWebKit/private/qquickwebpage_p.h>
216@@ -140,20 +139,12 @@
217
218 fbo.release();
219
220+ const QUrl& url = m_webview->url();
221 QImage image = fbo.toImage().scaled(m_targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
222
223- WebThumbnailUtils::ensureCacheLocation();
224- QUrl url = m_webview->url();
225- QFileInfo thumbnail = WebThumbnailUtils::thumbnailFile(url);
226- bool saved = image.save(thumbnail.absoluteFilePath());
227-
228- // Make a link to the thumbnail file for the corresponding domain’s thumbnail.
229- QUrl domain(DomainUtils::extractTopLevelDomainName(url));
230- QString domainThumbnail = WebThumbnailUtils::thumbnailFile(domain).absoluteFilePath();
231- if (QFile::exists(domainThumbnail)) {
232- QFile::remove(domainThumbnail);
233- }
234- QFile::link(thumbnail.fileName(), domainThumbnail);
235+ // Invoke the method asynchronously.
236+ QMetaObject::invokeMethod(&WebThumbnailUtils::instance(), "cacheThumbnail",
237+ Qt::QueuedConnection, Q_ARG(QUrl, url), Q_ARG(QImage, image));
238
239 root.removeChildNode(node);
240
241@@ -165,9 +156,7 @@
242 }
243 }
244
245- if (saved) {
246- Q_EMIT thumbnailRendered(url);
247- }
248+ Q_EMIT thumbnailRendered(url);
249
250 return oldNode;
251 }
252
253=== modified file 'tests/unittests/history-domainlist-chronological-model/CMakeLists.txt'
254--- tests/unittests/history-domainlist-chronological-model/CMakeLists.txt 2013-07-29 10:32:13 +0000
255+++ tests/unittests/history-domainlist-chronological-model/CMakeLists.txt 2013-08-09 07:47:22 +0000
256@@ -6,7 +6,6 @@
257 ${webbrowser-plugin_SOURCE_DIR}/history-domainlist-chronological-model.cpp
258 ${webbrowser-plugin_SOURCE_DIR}/history-model.cpp
259 ${webbrowser-plugin_SOURCE_DIR}/history-timeframe-model.cpp
260- ${webbrowser-plugin_SOURCE_DIR}/webthumbnail-utils.cpp
261 tst_HistoryDomainListChronologicalModelTests.cpp
262 )
263 add_executable(${TEST} ${SOURCES})
264
265=== modified file 'tests/unittests/history-domainlist-model/CMakeLists.txt'
266--- tests/unittests/history-domainlist-model/CMakeLists.txt 2013-07-29 10:32:13 +0000
267+++ tests/unittests/history-domainlist-model/CMakeLists.txt 2013-08-09 07:47:22 +0000
268@@ -5,7 +5,6 @@
269 ${webbrowser-plugin_SOURCE_DIR}/history-domainlist-model.cpp
270 ${webbrowser-plugin_SOURCE_DIR}/history-model.cpp
271 ${webbrowser-plugin_SOURCE_DIR}/history-timeframe-model.cpp
272- ${webbrowser-plugin_SOURCE_DIR}/webthumbnail-utils.cpp
273 tst_HistoryDomainListModelTests.cpp
274 )
275 add_executable(${TEST} ${SOURCES})

Subscribers

People subscribed via source and target branches

to status/vote changes: