Merge lp:~osomon/webbrowser-app/disable-favicon-provider-rtm into lp:webbrowser-app/rtm-14.09
- disable-favicon-provider-rtm
- Merge into rtm-14.09
Proposed by
Olivier Tilloy
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Olivier Tilloy | ||||
Approved revision: | 776 | ||||
Merged at revision: | 775 | ||||
Proposed branch: | lp:~osomon/webbrowser-app/disable-favicon-provider-rtm | ||||
Merge into: | lp:webbrowser-app/rtm-14.09 | ||||
Diff against target: |
1189 lines (+566/-443) 17 files modified
src/Ubuntu/Components/Extras/Browser/CMakeLists.txt (+2/-2) src/Ubuntu/Web/CMakeLists.txt (+2/-2) src/Ubuntu/Web/favicon-image-provider.cpp (+0/-125) src/Ubuntu/Web/favicon-image-provider.h (+0/-47) src/Ubuntu/Web/plugin.cpp (+0/-3) src/app/CMakeLists.txt (+2/-1) src/app/Favicon.qml (+9/-5) src/app/browserapplication.cpp (+2/-0) src/app/favicon-fetcher.cpp (+167/-0) src/app/favicon-fetcher.h (+70/-0) tests/unittests/CMakeLists.txt (+1/-1) tests/unittests/favicon-fetcher/CMakeLists.txt (+10/-0) tests/unittests/favicon-fetcher/tst_FaviconFetcherTests.cpp (+280/-0) tests/unittests/favicon-image-provider/CMakeLists.txt (+0/-10) tests/unittests/favicon-image-provider/tst_FaviconImageProviderTests.cpp (+0/-242) tests/unittests/qml/CMakeLists.txt (+7/-2) tests/unittests/qml/tst_QmlTests.cpp (+14/-3) |
||||
To merge this branch: | bzr merge lp:~osomon/webbrowser-app/disable-favicon-provider-rtm | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Bill Filler (community) | Needs Fixing | ||
Review via email: mp+242757@code.launchpad.net |
Commit message
Replace the custom favicon image provider with a FaviconFetcher component that better handles cancelling pending requests.
Description of the change
To post a comment you must log in.
- 776. By Olivier Tilloy
-
Replace the custom favicon image provider with a FaviconFetcher component that better handles cancelling pending requests.
Revision history for this message
Olivier Tilloy (osomon) wrote : | # |
Following Florian’s suggestions, I replaced the custom favicon image provider with a FaviconFetcher component that better handles cancelling pending requests. It is fully unit-tested, including the use-case of emitting numerous queries in a row and ensuring that they are cancelled as a new one comes in.
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/CMakeLists.txt' |
2 | --- src/Ubuntu/Components/Extras/Browser/CMakeLists.txt 2014-07-31 15:51:10 +0000 |
3 | +++ src/Ubuntu/Components/Extras/Browser/CMakeLists.txt 2014-11-27 10:18:45 +0000 |
4 | @@ -4,11 +4,11 @@ |
5 | |
6 | set(PLUGIN ubuntu-ui-extras-browser-plugin) |
7 | |
8 | -set(PLUGIN_SRC favicon-image-provider.cpp plugin.cpp) |
9 | +set(PLUGIN_SRC plugin.cpp) |
10 | |
11 | add_library(${PLUGIN} MODULE ${PLUGIN_SRC}) |
12 | |
13 | -qt5_use_modules(${PLUGIN} Core Gui Network Qml Quick) |
14 | +qt5_use_modules(${PLUGIN} Core Gui Qml) |
15 | |
16 | file(GLOB QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml qmldir *.js) |
17 | install(TARGETS ${PLUGIN} DESTINATION ${WEBBROWSER_IMPORTS_DIR}) |
18 | |
19 | === modified file 'src/Ubuntu/Web/CMakeLists.txt' |
20 | --- src/Ubuntu/Web/CMakeLists.txt 2014-07-31 15:51:10 +0000 |
21 | +++ src/Ubuntu/Web/CMakeLists.txt 2014-11-27 10:18:45 +0000 |
22 | @@ -4,11 +4,11 @@ |
23 | |
24 | set(PLUGIN ubuntu-web-plugin) |
25 | |
26 | -set(PLUGIN_SRC favicon-image-provider.cpp plugin.cpp) |
27 | +set(PLUGIN_SRC plugin.cpp) |
28 | |
29 | add_library(${PLUGIN} MODULE ${PLUGIN_SRC}) |
30 | |
31 | -qt5_use_modules(${PLUGIN} Core Gui Network Qml Quick) |
32 | +qt5_use_modules(${PLUGIN} Core Gui Qml) |
33 | |
34 | file(GLOB QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml qmldir *.js) |
35 | install(TARGETS ${PLUGIN} DESTINATION ${UBUNTU_WEB_IMPORTS_DIR}) |
36 | |
37 | === removed file 'src/Ubuntu/Web/favicon-image-provider.cpp' |
38 | --- src/Ubuntu/Web/favicon-image-provider.cpp 2014-09-30 10:40:46 +0000 |
39 | +++ src/Ubuntu/Web/favicon-image-provider.cpp 1970-01-01 00:00:00 +0000 |
40 | @@ -1,125 +0,0 @@ |
41 | -/* |
42 | - * Copyright 2014 Canonical Ltd. |
43 | - * |
44 | - * This file is part of webbrowser-app. |
45 | - * |
46 | - * webbrowser-app is free software; you can redistribute it and/or modify |
47 | - * it under the terms of the GNU General Public License as published by |
48 | - * the Free Software Foundation; version 3. |
49 | - * |
50 | - * webbrowser-app is distributed in the hope that it will be useful, |
51 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
52 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
53 | - * GNU General Public License for more details. |
54 | - * |
55 | - * You should have received a copy of the GNU General Public License |
56 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
57 | - */ |
58 | - |
59 | -#include "favicon-image-provider.h" |
60 | - |
61 | -// Qt |
62 | -#include <QtCore/QCryptographicHash> |
63 | -#include <QtCore/QDebug> |
64 | -#include <QtCore/QDir> |
65 | -#include <QtCore/QEventLoop> |
66 | -#include <QtCore/QFileInfo> |
67 | -#include <QtCore/QStandardPaths> |
68 | -#include <QtCore/QUrl> |
69 | -#include <QtNetwork/QNetworkAccessManager> |
70 | -#include <QtNetwork/QNetworkReply> |
71 | -#include <QtNetwork/QNetworkRequest> |
72 | - |
73 | -#define MAX_REDIRECTIONS 5 |
74 | -#define CACHE_EXPIRATION_DAYS 100 |
75 | - |
76 | -FaviconImageProvider::FaviconImageProvider() |
77 | - : QQuickImageProvider(QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading) |
78 | -{ |
79 | - QDir cacheLocation(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/favicons"); |
80 | - m_cacheLocation = cacheLocation.absolutePath(); |
81 | - if (!cacheLocation.exists()) { |
82 | - QDir::root().mkpath(m_cacheLocation); |
83 | - } |
84 | -} |
85 | - |
86 | -const QString& FaviconImageProvider::cacheLocation() const |
87 | -{ |
88 | - return m_cacheLocation; |
89 | -} |
90 | - |
91 | -QImage FaviconImageProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize) |
92 | -{ |
93 | - if (id.isEmpty()) { |
94 | - return QImage(); |
95 | - } |
96 | - |
97 | - QString extension; |
98 | - int extensionIndex = id.lastIndexOf("."); |
99 | - if (extensionIndex != -1) { |
100 | - extension = id.mid(extensionIndex); |
101 | - } |
102 | - QString hash(QCryptographicHash::hash(id.toUtf8(), QCryptographicHash::Md5).toHex()); |
103 | - QString filepath = m_cacheLocation + "/" + hash + extension; |
104 | - |
105 | - QImage image; |
106 | - QFileInfo fileinfo(filepath); |
107 | - if (fileinfo.exists()) { |
108 | - if (fileinfo.lastModified().daysTo(QDateTime::currentDateTime()) > CACHE_EXPIRATION_DAYS) { |
109 | - image = downloadImage(id); |
110 | - if (!image.isNull()) { |
111 | - image.save(filepath); |
112 | - } |
113 | - } else { |
114 | - image.load(filepath); |
115 | - } |
116 | - } else { |
117 | - image = downloadImage(id); |
118 | - if (!image.isNull()) { |
119 | - image.save(filepath); |
120 | - } |
121 | - } |
122 | - |
123 | - if (!image.isNull()) { |
124 | - *size = image.size(); |
125 | - } |
126 | - if (!image.isNull() && requestedSize.isValid() && (image.size() != requestedSize)) { |
127 | - return image.scaled(requestedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); |
128 | - } else { |
129 | - return image; |
130 | - } |
131 | -} |
132 | - |
133 | -QImage FaviconImageProvider::downloadImage(const QUrl& url) |
134 | -{ |
135 | - if (!m_manager) { |
136 | - m_manager.reset(new QNetworkAccessManager()); |
137 | - } |
138 | - QUrl currentUrl(url); |
139 | - int redirections = 0; |
140 | - while (redirections < MAX_REDIRECTIONS) { |
141 | - QNetworkRequest request(currentUrl); |
142 | - request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); |
143 | - QEventLoop loop; |
144 | - QObject::connect(m_manager.data(), SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit())); |
145 | - QNetworkReply* reply = m_manager->get(request); |
146 | - loop.exec(); |
147 | - currentUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); |
148 | - if (currentUrl.isEmpty()) { |
149 | - if (reply->error() != QNetworkReply::NoError) { |
150 | - qWarning() << "Failed to download" << url << ":" << reply->errorString(); |
151 | - delete reply; |
152 | - return QImage(); |
153 | - } else { |
154 | - QByteArray data = reply->readAll(); |
155 | - delete reply; |
156 | - return QImage::fromData(data); |
157 | - } |
158 | - } else { |
159 | - delete reply; |
160 | - ++redirections; |
161 | - } |
162 | - } |
163 | - qWarning() << "Failed to download" << url << ": too many redirections"; |
164 | - return QImage(); |
165 | -} |
166 | |
167 | === removed file 'src/Ubuntu/Web/favicon-image-provider.h' |
168 | --- src/Ubuntu/Web/favicon-image-provider.h 2014-09-30 10:40:46 +0000 |
169 | +++ src/Ubuntu/Web/favicon-image-provider.h 1970-01-01 00:00:00 +0000 |
170 | @@ -1,47 +0,0 @@ |
171 | -/* |
172 | - * Copyright 2014 Canonical Ltd. |
173 | - * |
174 | - * This file is part of webbrowser-app. |
175 | - * |
176 | - * webbrowser-app is free software; you can redistribute it and/or modify |
177 | - * it under the terms of the GNU General Public License as published by |
178 | - * the Free Software Foundation; version 3. |
179 | - * |
180 | - * webbrowser-app is distributed in the hope that it will be useful, |
181 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
182 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
183 | - * GNU General Public License for more details. |
184 | - * |
185 | - * You should have received a copy of the GNU General Public License |
186 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
187 | - */ |
188 | - |
189 | -#ifndef __FAVICON_IMAGE_PROVIDER_H__ |
190 | -#define __FAVICON_IMAGE_PROVIDER_H__ |
191 | - |
192 | -// Qt |
193 | -#include <QtCore/QScopedPointer> |
194 | -#include <QtCore/QString> |
195 | -#include <QtCore/QtGlobal> |
196 | -#include <QtQuick/QQuickImageProvider> |
197 | - |
198 | -class QNetworkAccessManager; |
199 | -class QUrl; |
200 | - |
201 | -class FaviconImageProvider Q_DECL_FINAL : public QQuickImageProvider |
202 | -{ |
203 | -public: |
204 | - FaviconImageProvider(); |
205 | - |
206 | - const QString& cacheLocation() const; |
207 | - |
208 | - QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize); |
209 | - |
210 | -private: |
211 | - QImage downloadImage(const QUrl& url); |
212 | - |
213 | - QString m_cacheLocation; |
214 | - QScopedPointer<QNetworkAccessManager> m_manager; |
215 | -}; |
216 | - |
217 | -#endif // __FAVICON_IMAGE_PROVIDER_H__ |
218 | |
219 | === modified file 'src/Ubuntu/Web/plugin.cpp' |
220 | --- src/Ubuntu/Web/plugin.cpp 2014-10-06 14:12:48 +0000 |
221 | +++ src/Ubuntu/Web/plugin.cpp 2014-11-27 10:18:45 +0000 |
222 | @@ -17,7 +17,6 @@ |
223 | */ |
224 | |
225 | #include "plugin.h" |
226 | -#include "favicon-image-provider.h" |
227 | |
228 | // Qt |
229 | #include <QtCore/QDir> |
230 | @@ -115,8 +114,6 @@ |
231 | context->setContextProperty("formFactor", getFormFactor()); |
232 | context->setContextProperty("webviewDevtoolsDebugPort", getDevtoolsPort()); |
233 | context->setContextProperty("webviewDevtoolsDebugHost", getDevtoolsHost()); |
234 | - |
235 | - engine->addImageProvider("favicon", new FaviconImageProvider()); |
236 | } |
237 | |
238 | void UbuntuBrowserPlugin::registerTypes(const char* uri) |
239 | |
240 | === modified file 'src/app/CMakeLists.txt' |
241 | --- src/app/CMakeLists.txt 2014-08-06 13:57:06 +0000 |
242 | +++ src/app/CMakeLists.txt 2014-11-27 10:18:45 +0000 |
243 | @@ -9,13 +9,14 @@ |
244 | |
245 | set(COMMONLIB_SRC |
246 | browserapplication.cpp |
247 | + favicon-fetcher.cpp |
248 | session-storage.cpp |
249 | webbrowser-window.cpp |
250 | ) |
251 | |
252 | add_library(${COMMONLIB} STATIC ${COMMONLIB_SRC}) |
253 | |
254 | -qt5_use_modules(${COMMONLIB} Core Network Qml Quick Widgets) |
255 | +qt5_use_modules(${COMMONLIB} Core Gui Network Qml Quick Widgets) |
256 | |
257 | include_directories(${Qt5Quick_PRIVATE_INCLUDE_DIRS}) |
258 | |
259 | |
260 | === modified file 'src/app/Favicon.qml' |
261 | --- src/app/Favicon.qml 2014-09-26 13:29:30 +0000 |
262 | +++ src/app/Favicon.qml 2014-11-27 10:18:45 +0000 |
263 | @@ -18,9 +18,10 @@ |
264 | |
265 | import QtQuick 2.0 |
266 | import Ubuntu.Components 1.1 |
267 | +import webbrowsercommon.private 0.1 |
268 | |
269 | Item { |
270 | - property url source |
271 | + property alias source: fetcher.url |
272 | property bool fallbackIcon: true |
273 | |
274 | width: units.dp(16) |
275 | @@ -28,15 +29,18 @@ |
276 | |
277 | Image { |
278 | id: image |
279 | - readonly property string url: parent.source.toString() |
280 | - source: url ? "image://favicon/" + url : "" |
281 | - |
282 | + source: fetcher.localUrl |
283 | anchors.fill: parent |
284 | } |
285 | |
286 | + FaviconFetcher { |
287 | + id: fetcher |
288 | + } |
289 | + |
290 | Icon { |
291 | anchors.fill: parent |
292 | name: "stock_website" |
293 | - visible: parent.fallbackIcon && (image.status !== Image.Ready) |
294 | + visible: parent.fallbackIcon && |
295 | + ((image.status !== Image.Ready) || !image.source.toString()) |
296 | } |
297 | } |
298 | |
299 | === modified file 'src/app/browserapplication.cpp' |
300 | --- src/app/browserapplication.cpp 2014-10-02 20:47:10 +0000 |
301 | +++ src/app/browserapplication.cpp 2014-11-27 10:18:45 +0000 |
302 | @@ -35,6 +35,7 @@ |
303 | // local |
304 | #include "browserapplication.h" |
305 | #include "config.h" |
306 | +#include "favicon-fetcher.h" |
307 | #include "session-storage.h" |
308 | #include "webbrowser-window.h" |
309 | |
310 | @@ -143,6 +144,7 @@ |
311 | } |
312 | |
313 | const char* uri = "webbrowsercommon.private"; |
314 | + qmlRegisterType<FaviconFetcher>(uri, 0, 1, "FaviconFetcher"); |
315 | qmlRegisterType<SessionStorage>(uri, 0, 1, "SessionStorage"); |
316 | |
317 | m_engine = new QQmlEngine; |
318 | |
319 | === added file 'src/app/favicon-fetcher.cpp' |
320 | --- src/app/favicon-fetcher.cpp 1970-01-01 00:00:00 +0000 |
321 | +++ src/app/favicon-fetcher.cpp 2014-11-27 10:18:45 +0000 |
322 | @@ -0,0 +1,167 @@ |
323 | +/* |
324 | + * Copyright 2014 Canonical Ltd. |
325 | + * |
326 | + * This file is part of webbrowser-app. |
327 | + * |
328 | + * webbrowser-app is free software; you can redistribute it and/or modify |
329 | + * it under the terms of the GNU General Public License as published by |
330 | + * the Free Software Foundation; version 3. |
331 | + * |
332 | + * webbrowser-app is distributed in the hope that it will be useful, |
333 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
334 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
335 | + * GNU General Public License for more details. |
336 | + * |
337 | + * You should have received a copy of the GNU General Public License |
338 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
339 | + */ |
340 | + |
341 | +#include "favicon-fetcher.h" |
342 | + |
343 | +// Qt |
344 | +#include <QtCore/QCryptographicHash> |
345 | +#include <QtCore/QDebug> |
346 | +#include <QtCore/QDir> |
347 | +#include <QtCore/QFileInfo> |
348 | +#include <QtCore/QMetaObject> |
349 | +#include <QtCore/QStandardPaths> |
350 | +#include <QtGui/QImage> |
351 | +#include <QtNetwork/QNetworkAccessManager> |
352 | +#include <QtNetwork/QNetworkReply> |
353 | +#include <QtNetwork/QNetworkRequest> |
354 | + |
355 | +#define MAX_REDIRECTIONS 5 |
356 | +#define CACHE_EXPIRATION_DAYS 100 |
357 | + |
358 | +FaviconFetcher::FaviconFetcher(QObject* parent) |
359 | + : QObject(parent) |
360 | + , m_reply(0) |
361 | +{ |
362 | + QDir cacheLocation(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/favicons"); |
363 | + m_cacheLocation = cacheLocation.absolutePath(); |
364 | + if (!cacheLocation.exists()) { |
365 | + QDir::root().mkpath(m_cacheLocation); |
366 | + } |
367 | +} |
368 | + |
369 | +FaviconFetcher::~FaviconFetcher() |
370 | +{ |
371 | + if (m_reply) { |
372 | + m_reply->abort(); |
373 | + delete m_reply; |
374 | + } |
375 | +} |
376 | + |
377 | +const QUrl& FaviconFetcher::url() const |
378 | +{ |
379 | + return m_url; |
380 | +} |
381 | + |
382 | +void FaviconFetcher::setUrl(const QUrl& url) |
383 | +{ |
384 | + if (url != m_url) { |
385 | + m_url = url; |
386 | + Q_EMIT urlChanged(); |
387 | + |
388 | + setLocalUrl(QUrl()); |
389 | + |
390 | + if (m_reply) { |
391 | + m_reply->abort(); |
392 | + m_reply = 0; |
393 | + } |
394 | + |
395 | + if (!url.isValid()) { |
396 | + return; |
397 | + } |
398 | + |
399 | + if (url.isLocalFile()) { |
400 | + setLocalUrl(url); |
401 | + return; |
402 | + } |
403 | + |
404 | + QString id = url.toString(QUrl::None); |
405 | + |
406 | + QString extension; |
407 | + int extensionIndex = id.lastIndexOf("."); |
408 | + if (extensionIndex != -1) { |
409 | + extension = id.mid(extensionIndex); |
410 | + } |
411 | + |
412 | + QString hash(QCryptographicHash::hash(id.toUtf8(), QCryptographicHash::Md5).toHex()); |
413 | + m_filepath = m_cacheLocation + "/" + hash + extension; |
414 | + |
415 | + QFileInfo fileinfo(m_filepath); |
416 | + if (fileinfo.exists() && (fileinfo.lastModified().daysTo(QDateTime::currentDateTime()) < CACHE_EXPIRATION_DAYS)) { |
417 | + setLocalUrl(QUrl::fromLocalFile(m_filepath)); |
418 | + } else { |
419 | + m_redirections = 0; |
420 | + download(url); |
421 | + } |
422 | + } |
423 | +} |
424 | + |
425 | +const QUrl& FaviconFetcher::localUrl() const |
426 | +{ |
427 | + return m_localUrl; |
428 | +} |
429 | + |
430 | +void FaviconFetcher::setLocalUrl(const QUrl& url) |
431 | +{ |
432 | + if (url != m_localUrl) { |
433 | + m_localUrl = url; |
434 | + Q_EMIT localUrlChanged(); |
435 | + } |
436 | +} |
437 | + |
438 | +const QString& FaviconFetcher::cacheLocation() const |
439 | +{ |
440 | + return m_cacheLocation; |
441 | +} |
442 | + |
443 | +void FaviconFetcher::download(const QUrl& url) |
444 | +{ |
445 | + if (!m_manager) { |
446 | + m_manager.reset(new QNetworkAccessManager()); |
447 | + connect(m_manager.data(), SIGNAL(finished(QNetworkReply*)), |
448 | + this, SLOT(downloadFinished(QNetworkReply*))); |
449 | + } |
450 | + QNetworkRequest request(url); |
451 | + request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); |
452 | + // For some reason slashdot.org closes the connection with the default |
453 | + // user agent string ("Mozilla/5.0"). Weird. |
454 | + request.setHeader(QNetworkRequest::UserAgentHeader, QString("Mozilla")); |
455 | + m_reply = m_manager->get(request); |
456 | +} |
457 | + |
458 | +void FaviconFetcher::downloadFinished(QNetworkReply* reply) |
459 | +{ |
460 | + if (reply->error() == QNetworkReply::OperationCanceledError) { |
461 | + reply->deleteLater(); |
462 | + return; |
463 | + } |
464 | + QUrl url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); |
465 | + if (url.isEmpty()) { |
466 | + if (reply->error() == QNetworkReply::NoError) { |
467 | + QByteArray data = reply->readAll(); |
468 | + if (QImage::fromData(data).save(m_filepath)) { |
469 | + setLocalUrl(QUrl::fromLocalFile(m_filepath)); |
470 | + } |
471 | + } else { |
472 | + qWarning() << "Failed to download" << reply->url() |
473 | + << ":" << reply->errorString(); |
474 | + } |
475 | + reply->deleteLater(); |
476 | + m_reply = 0; |
477 | + } else { |
478 | + reply->deleteLater(); |
479 | + m_reply = 0; |
480 | + if (++m_redirections < MAX_REDIRECTIONS) { |
481 | + QMetaObject::invokeMethod(this, "download", |
482 | + Qt::QueuedConnection, |
483 | + Q_ARG(QUrl, url)); |
484 | + } else { |
485 | + qWarning() << "Failed to download" << m_url |
486 | + << ": too many redirections"; |
487 | + } |
488 | + } |
489 | +} |
490 | |
491 | === added file 'src/app/favicon-fetcher.h' |
492 | --- src/app/favicon-fetcher.h 1970-01-01 00:00:00 +0000 |
493 | +++ src/app/favicon-fetcher.h 2014-11-27 10:18:45 +0000 |
494 | @@ -0,0 +1,70 @@ |
495 | +/* |
496 | + * Copyright 2014 Canonical Ltd. |
497 | + * |
498 | + * This file is part of webbrowser-app. |
499 | + * |
500 | + * webbrowser-app is free software; you can redistribute it and/or modify |
501 | + * it under the terms of the GNU General Public License as published by |
502 | + * the Free Software Foundation; version 3. |
503 | + * |
504 | + * webbrowser-app is distributed in the hope that it will be useful, |
505 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
506 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
507 | + * GNU General Public License for more details. |
508 | + * |
509 | + * You should have received a copy of the GNU General Public License |
510 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
511 | + */ |
512 | + |
513 | +#ifndef __FAVICON_FETCHER_H__ |
514 | +#define __FAVICON_FETCHER_H__ |
515 | + |
516 | +// Qt |
517 | +#include <QtCore/QScopedPointer> |
518 | +#include <QtCore/QObject> |
519 | +#include <QtCore/QString> |
520 | +#include <QtCore/QtGlobal> |
521 | +#include <QtCore/QUrl> |
522 | + |
523 | +class QNetworkAccessManager; |
524 | +class QNetworkReply; |
525 | + |
526 | +class FaviconFetcher Q_DECL_FINAL : public QObject |
527 | +{ |
528 | + Q_OBJECT |
529 | + |
530 | + Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) |
531 | + Q_PROPERTY(QUrl localUrl READ localUrl NOTIFY localUrlChanged) |
532 | + |
533 | +public: |
534 | + FaviconFetcher(QObject* parent=0); |
535 | + ~FaviconFetcher(); |
536 | + |
537 | + const QUrl& url() const; |
538 | + void setUrl(const QUrl& url); |
539 | + |
540 | + const QUrl& localUrl() const; |
541 | + |
542 | + const QString& cacheLocation() const; |
543 | + |
544 | +Q_SIGNALS: |
545 | + void urlChanged() const; |
546 | + void localUrlChanged() const; |
547 | + |
548 | +private Q_SLOTS: |
549 | + void download(const QUrl& url); |
550 | + void downloadFinished(QNetworkReply* reply); |
551 | + |
552 | +private: |
553 | + void setLocalUrl(const QUrl& url); |
554 | + |
555 | + QString m_cacheLocation; |
556 | + QScopedPointer<QNetworkAccessManager> m_manager; |
557 | + QNetworkReply* m_reply; |
558 | + QUrl m_url; |
559 | + QString m_filepath; |
560 | + int m_redirections; |
561 | + QUrl m_localUrl; |
562 | +}; |
563 | + |
564 | +#endif // __FAVICON_FETCHER_H__ |
565 | |
566 | === modified file 'tests/unittests/CMakeLists.txt' |
567 | --- tests/unittests/CMakeLists.txt 2014-10-03 14:41:48 +0000 |
568 | +++ tests/unittests/CMakeLists.txt 2014-11-27 10:18:45 +0000 |
569 | @@ -15,4 +15,4 @@ |
570 | add_subdirectory(cookie-store) |
571 | add_subdirectory(oxide-cookie-helper) |
572 | add_subdirectory(session-storage) |
573 | -add_subdirectory(favicon-image-provider) |
574 | +add_subdirectory(favicon-fetcher) |
575 | |
576 | === added directory 'tests/unittests/favicon-fetcher' |
577 | === added file 'tests/unittests/favicon-fetcher/CMakeLists.txt' |
578 | --- tests/unittests/favicon-fetcher/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
579 | +++ tests/unittests/favicon-fetcher/CMakeLists.txt 2014-11-27 10:18:45 +0000 |
580 | @@ -0,0 +1,10 @@ |
581 | +set(TEST tst_FaviconFetcherTests) |
582 | +set(SOURCES |
583 | + ${webbrowser-common_SOURCE_DIR}/favicon-fetcher.cpp |
584 | + tst_FaviconFetcherTests.cpp |
585 | +) |
586 | +add_executable(${TEST} ${SOURCES}) |
587 | +include_directories(${webbrowser-common_SOURCE_DIR}) |
588 | +qt5_use_modules(${TEST} Core Gui Network Test) |
589 | +add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml) |
590 | +set_tests_properties(${TEST} PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal") |
591 | |
592 | === added file 'tests/unittests/favicon-fetcher/tst_FaviconFetcherTests.cpp' |
593 | --- tests/unittests/favicon-fetcher/tst_FaviconFetcherTests.cpp 1970-01-01 00:00:00 +0000 |
594 | +++ tests/unittests/favicon-fetcher/tst_FaviconFetcherTests.cpp 2014-11-27 10:18:45 +0000 |
595 | @@ -0,0 +1,280 @@ |
596 | +/* |
597 | + * Copyright 2014 Canonical Ltd. |
598 | + * |
599 | + * This file is part of webbrowser-app. |
600 | + * |
601 | + * webbrowser-app is free software; you can redistribute it and/or modify |
602 | + * it under the terms of the GNU General Public License as published by |
603 | + * the Free Software Foundation; version 3. |
604 | + * |
605 | + * webbrowser-app is distributed in the hope that it will be useful, |
606 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
607 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
608 | + * GNU General Public License for more details. |
609 | + * |
610 | + * You should have received a copy of the GNU General Public License |
611 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
612 | + */ |
613 | + |
614 | +// system |
615 | +#include <utime.h> |
616 | + |
617 | +// Qt |
618 | +#include <QtCore/QDateTime> |
619 | +#include <QtCore/QDir> |
620 | +#include <QtCore/QRegExp> |
621 | +#include <QtCore/QString> |
622 | +#include <QtCore/QStringList> |
623 | +#include <QtCore/QTextStream> |
624 | +#include <QtNetwork/QTcpServer> |
625 | +#include <QtNetwork/QTcpSocket> |
626 | +#include <QtTest/QSignalSpy> |
627 | +#include <QtTest/QtTest> |
628 | + |
629 | +// local |
630 | +#include "favicon-fetcher.h" |
631 | + |
632 | +const char icon_data[] = { |
633 | + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x00, |
634 | + 0x01, 0x00, 0x38, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, |
635 | + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, |
636 | + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
637 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
638 | + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
639 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
640 | +}; |
641 | +const int icon_data_size = 78; |
642 | + |
643 | +class TestHTTPServer : public QTcpServer |
644 | +{ |
645 | + Q_OBJECT |
646 | + |
647 | +public: |
648 | + TestHTTPServer(QObject* parent = 0) |
649 | + : QTcpServer(parent) |
650 | + {} |
651 | + |
652 | + QString baseURL() const |
653 | + { |
654 | + return "http://" + serverAddress().toString() + ":" + QString::number(serverPort()); |
655 | + } |
656 | + |
657 | +Q_SIGNALS: |
658 | + void gotRequest(const QString& path) const; |
659 | + |
660 | +protected: |
661 | + void incomingConnection(qintptr socketDescriptor) |
662 | + { |
663 | + QTcpSocket* socket = new QTcpSocket(this); |
664 | + connect(socket, SIGNAL(readyRead()), SLOT(readClient())); |
665 | + connect(socket, SIGNAL(disconnected()), SLOT(discardClient())); |
666 | + socket->setSocketDescriptor(socketDescriptor); |
667 | + } |
668 | + |
669 | +private Q_SLOTS: |
670 | + void readClient() |
671 | + { |
672 | + QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender()); |
673 | + if (!socket) { |
674 | + return; |
675 | + } |
676 | + if (!socket->canReadLine()) { |
677 | + return; |
678 | + } |
679 | + QStringList tokens = QString(socket->readLine()).split(QRegExp("[ \r\n][ \r\n]*")); |
680 | + if (tokens.isEmpty()) { |
681 | + return; |
682 | + } |
683 | + if (tokens.first() != "GET") { |
684 | + return; |
685 | + } |
686 | + QString path = tokens[1]; |
687 | + Q_EMIT gotRequest(path); |
688 | + QTextStream response(socket); |
689 | + response.setAutoDetectUnicode(true); |
690 | + QRegExp icon("/\\w+\\.ico"); |
691 | + QRegExp redirection("^/redirect/(\\d+)/(.*)"); |
692 | + if (icon.exactMatch(path)) { |
693 | + response << "HTTP/1.0 200 OK\r\n" |
694 | + << "Content-Length: " << icon_data_size << "\r\n" |
695 | + << "Content-Type: image/x-icon\r\n\r\n" |
696 | + << QString::fromLocal8Bit(icon_data, icon_data_size) << "\n"; |
697 | + } else if (redirection.exactMatch(path)) { |
698 | + int n = redirection.cap(1).toInt(); |
699 | + response << "HTTP/1.0 303 See Other\r\n" |
700 | + << "Content-Length: 9\r\n" |
701 | + << "Content-Type: text/plain\r\n" |
702 | + << "Location: " << baseURL(); |
703 | + if (n == 1) { |
704 | + response << "/" << redirection.cap(2); |
705 | + } else { |
706 | + response << "/redirect/" << (n - 1) << "/" << redirection.cap(2); |
707 | + } |
708 | + response << "\r\n\r\n" |
709 | + << "see other\n"; |
710 | + } else { |
711 | + response << "HTTP/1.0 404 Not Found\r\n" |
712 | + << "Content-Length: 9\r\n" |
713 | + << "Content-Type: text/plain\r\n\r\n" |
714 | + << "not found\n"; |
715 | + } |
716 | + response.flush(); |
717 | + socket->waitForBytesWritten(); |
718 | + socket->disconnectFromHost(); |
719 | + } |
720 | + |
721 | + void discardClient() |
722 | + { |
723 | + QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender()); |
724 | + if (socket) { |
725 | + socket->deleteLater(); |
726 | + } |
727 | + } |
728 | +}; |
729 | + |
730 | +class FaviconFetcherTests : public QObject |
731 | +{ |
732 | + Q_OBJECT |
733 | + |
734 | +private: |
735 | + FaviconFetcher* fetcher; |
736 | + QSignalSpy* fetcherSpy; |
737 | + TestHTTPServer* server; |
738 | + QSignalSpy* serverSpy; |
739 | + |
740 | +private Q_SLOTS: |
741 | + void init() |
742 | + { |
743 | + { |
744 | + FaviconFetcher temp; |
745 | + QDir(temp.cacheLocation()).removeRecursively(); |
746 | + } |
747 | + fetcher = new FaviconFetcher; |
748 | + fetcherSpy = new QSignalSpy(fetcher, SIGNAL(localUrlChanged())); |
749 | + server = new TestHTTPServer; |
750 | + server->listen(); |
751 | + serverSpy = new QSignalSpy(server, SIGNAL(gotRequest(const QString&))); |
752 | + } |
753 | + |
754 | + void cleanup() |
755 | + { |
756 | + delete serverSpy; |
757 | + delete server; |
758 | + delete fetcherSpy; |
759 | + delete fetcher; |
760 | + } |
761 | + |
762 | + void shouldCacheIcon() |
763 | + { |
764 | + QUrl url(server->baseURL() + "/favicon1.ico"); |
765 | + fetcher->setUrl(url); |
766 | + QCOMPARE(fetcher->url(), url); |
767 | + QVERIFY(fetcherSpy->wait()); |
768 | + QCOMPARE(serverSpy->count(), 1); |
769 | + QString cached = fetcher->localUrl().path(); |
770 | + QVERIFY(cached.startsWith(fetcher->cacheLocation())); |
771 | + QVERIFY(QFileInfo::exists(cached)); |
772 | + } |
773 | + |
774 | + void shouldNotCacheLocalIcon() |
775 | + { |
776 | + QUrl url("file:///tmp/favicon.ico"); |
777 | + fetcher->setUrl(url); |
778 | + QCOMPARE(fetcherSpy->count(), 1); |
779 | + QVERIFY(serverSpy->isEmpty()); |
780 | + QCOMPARE(fetcher->localUrl(), url); |
781 | + QDir cache(fetcher->cacheLocation(), "", QDir::Unsorted, QDir::Files | QDir::NoDotAndDotDot); |
782 | + QCOMPARE(cache.count(), (uint) 0); |
783 | + } |
784 | + |
785 | + void shouldNotCacheInvalidIcon() |
786 | + { |
787 | + // First fetch a valid icon to ensure localUrl is initially not empty |
788 | + QUrl url(server->baseURL() + "/favicon1.ico"); |
789 | + fetcher->setUrl(url); |
790 | + QVERIFY(fetcherSpy->wait()); |
791 | + QVERIFY(!fetcher->localUrl().isEmpty()); |
792 | + // Then request an invalid one |
793 | + url = QUrl(server->baseURL() + "/invalid"); |
794 | + fetcher->setUrl(url); |
795 | + QVERIFY(serverSpy->wait()); |
796 | + QVERIFY(fetcher->localUrl().isEmpty()); |
797 | + } |
798 | + |
799 | + void shouldReturnCachedIcon() |
800 | + { |
801 | + // First fetch an icon so that it’s cached |
802 | + QUrl url(server->baseURL() + "/favicon1.ico"); |
803 | + fetcher->setUrl(url); |
804 | + QVERIFY(fetcherSpy->wait()); |
805 | + QUrl localUrl = fetcher->localUrl(); |
806 | + QVERIFY(!localUrl.isEmpty()); |
807 | + // Then fetch another icon |
808 | + fetcher->setUrl(QUrl(server->baseURL() + "/favicon2.ico")); |
809 | + QVERIFY(fetcherSpy->wait()); |
810 | + QVERIFY(!fetcher->localUrl().isEmpty()); |
811 | + // Then fetch the first icon again, and verify it comes from the cache |
812 | + serverSpy->clear(); |
813 | + fetcher->setUrl(url); |
814 | + QCOMPARE(fetcher->localUrl(), localUrl); |
815 | + QVERIFY(serverSpy->isEmpty()); |
816 | + } |
817 | + |
818 | + void shouldHandleRedirections() |
819 | + { |
820 | + QUrl url(server->baseURL() + "/redirect/3/favicon1.ico"); |
821 | + fetcher->setUrl(url); |
822 | + QVERIFY(fetcherSpy->wait()); |
823 | + QCOMPARE(serverSpy->count(), 4); |
824 | + } |
825 | + |
826 | + void shouldNotHandleTooManyRedirections() |
827 | + { |
828 | + QUrl url(server->baseURL() + "/redirect/8/favicon1.ico"); |
829 | + fetcher->setUrl(url); |
830 | + for (int i = 0; i < 5; ++i) |
831 | + QVERIFY(!fetcherSpy->wait(500)); |
832 | + QCOMPARE(serverSpy->count(), 5); |
833 | + } |
834 | + |
835 | + void shouldDiscardOldCachedIcons() |
836 | + { |
837 | + // First fetch an icon, and touch the cached file on disk to ensure |
838 | + // it will be considered out of date next time it’s requested |
839 | + QUrl url(server->baseURL() + "/favicon1.ico"); |
840 | + fetcher->setUrl(url); |
841 | + QVERIFY(fetcherSpy->wait()); |
842 | + QUrl localUrl = fetcher->localUrl(); |
843 | + struct utimbuf ubuf; |
844 | + ubuf.modtime = QDateTime::currentDateTime().addYears(-1).toTime_t(); |
845 | + QCOMPARE(utime(localUrl.path().toUtf8().constData(), &ubuf), 0); |
846 | + // Then fetch another icon |
847 | + fetcher->setUrl(QUrl(server->baseURL() + "/favicon2.ico")); |
848 | + QVERIFY(fetcherSpy->wait()); |
849 | + QVERIFY(!fetcher->localUrl().isEmpty()); |
850 | + // Then fetch the first icon again, and verify it is being re-downloaded |
851 | + serverSpy->clear(); |
852 | + fetcher->setUrl(url); |
853 | + QVERIFY(fetcherSpy->wait()); |
854 | + QCOMPARE(fetcher->localUrl(), localUrl); |
855 | + QCOMPARE(serverSpy->count(), 1); |
856 | + } |
857 | + |
858 | + void shouldCancelRequests() |
859 | + { |
860 | + // Issue several requests rapidly in succession, and verify that |
861 | + // all the previous ones are discarded |
862 | + for (int i = 1; i < 10; ++i) { |
863 | + QUrl url(server->baseURL() + "/favicon" + QString::number(i) + ".ico"); |
864 | + fetcher->setUrl(url); |
865 | + QVERIFY(serverSpy->wait()); |
866 | + } |
867 | + QVERIFY(fetcherSpy->wait()); |
868 | + QCOMPARE(serverSpy->count(), 9); |
869 | + QCOMPARE(fetcherSpy->count(), 1); |
870 | + } |
871 | +}; |
872 | + |
873 | +QTEST_MAIN(FaviconFetcherTests) |
874 | + |
875 | +#include "tst_FaviconFetcherTests.moc" |
876 | |
877 | === removed directory 'tests/unittests/favicon-image-provider' |
878 | === removed file 'tests/unittests/favicon-image-provider/CMakeLists.txt' |
879 | --- tests/unittests/favicon-image-provider/CMakeLists.txt 2014-09-30 11:52:33 +0000 |
880 | +++ tests/unittests/favicon-image-provider/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
881 | @@ -1,10 +0,0 @@ |
882 | -set(TEST tst_FaviconImageProviderTests) |
883 | -set(SOURCES |
884 | - ${ubuntu-web-plugin_SOURCE_DIR}/favicon-image-provider.cpp |
885 | - tst_FaviconImageProviderTests.cpp |
886 | -) |
887 | -add_executable(${TEST} ${SOURCES}) |
888 | -include_directories(${ubuntu-web-plugin_SOURCE_DIR}) |
889 | -qt5_use_modules(${TEST} Core Quick Network Test) |
890 | -add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml) |
891 | -set_tests_properties(${TEST} PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal") |
892 | |
893 | === removed file 'tests/unittests/favicon-image-provider/tst_FaviconImageProviderTests.cpp' |
894 | --- tests/unittests/favicon-image-provider/tst_FaviconImageProviderTests.cpp 2014-09-30 10:40:46 +0000 |
895 | +++ tests/unittests/favicon-image-provider/tst_FaviconImageProviderTests.cpp 1970-01-01 00:00:00 +0000 |
896 | @@ -1,242 +0,0 @@ |
897 | -/* |
898 | - * Copyright 2014 Canonical Ltd. |
899 | - * |
900 | - * This file is part of webbrowser-app. |
901 | - * |
902 | - * webbrowser-app is free software; you can redistribute it and/or modify |
903 | - * it under the terms of the GNU General Public License as published by |
904 | - * the Free Software Foundation; version 3. |
905 | - * |
906 | - * webbrowser-app is distributed in the hope that it will be useful, |
907 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
908 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
909 | - * GNU General Public License for more details. |
910 | - * |
911 | - * You should have received a copy of the GNU General Public License |
912 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
913 | - */ |
914 | - |
915 | -// system |
916 | -#include <utime.h> |
917 | - |
918 | -// Qt |
919 | -#include <QtCore/QDateTime> |
920 | -#include <QtCore/QDir> |
921 | -#include <QtCore/QRegExp> |
922 | -#include <QtCore/QString> |
923 | -#include <QtCore/QTextStream> |
924 | -#include <QtNetwork/QNetworkAccessManager> |
925 | -#include <QtNetwork/QTcpServer> |
926 | -#include <QtTest/QtTest> |
927 | - |
928 | -// local |
929 | -#include "favicon-image-provider.h" |
930 | - |
931 | -const char icon_data[] = { |
932 | - 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x00, |
933 | - 0x01, 0x00, 0x38, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, |
934 | - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, |
935 | - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
936 | - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
937 | - 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
938 | - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
939 | -}; |
940 | -const int icon_data_size = 78; |
941 | - |
942 | -class TestHTTPServer : public QTcpServer |
943 | -{ |
944 | - Q_OBJECT |
945 | - |
946 | -public: |
947 | - QStringList requests; |
948 | - |
949 | - TestHTTPServer(QObject* parent = 0) |
950 | - : QTcpServer(parent) |
951 | - {} |
952 | - |
953 | - QString baseURL() const |
954 | - { |
955 | - return "http://" + serverAddress().toString() + ":" + QString::number(serverPort()); |
956 | - } |
957 | - |
958 | -protected: |
959 | - void incomingConnection(qintptr socketDescriptor) |
960 | - { |
961 | - QTcpSocket* socket = new QTcpSocket(this); |
962 | - connect(socket, SIGNAL(readyRead()), SLOT(readClient())); |
963 | - connect(socket, SIGNAL(disconnected()), SLOT(discardClient())); |
964 | - socket->setSocketDescriptor(socketDescriptor); |
965 | - } |
966 | - |
967 | -private Q_SLOTS: |
968 | - void readClient() |
969 | - { |
970 | - QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender()); |
971 | - if (!socket) { |
972 | - return; |
973 | - } |
974 | - if (!socket->canReadLine()) { |
975 | - return; |
976 | - } |
977 | - QStringList tokens = QString(socket->readLine()).split(QRegExp("[ \r\n][ \r\n]*")); |
978 | - if (tokens.isEmpty()) { |
979 | - return; |
980 | - } |
981 | - if (tokens.first() != "GET") { |
982 | - return; |
983 | - } |
984 | - QString path = tokens[1]; |
985 | - requests << path; |
986 | - QTextStream response(socket); |
987 | - response.setAutoDetectUnicode(true); |
988 | - QRegExp icon("/\\w+\\.ico"); |
989 | - QRegExp redirection("^/redirect/(\\d+)/(.*)"); |
990 | - if (icon.exactMatch(path)) { |
991 | - response << "HTTP/1.0 200 OK\r\n" |
992 | - << "Content-Length: " << icon_data_size << "\r\n" |
993 | - << "Content-Type: image/x-icon\r\n\r\n" |
994 | - << QString::fromLocal8Bit(icon_data, icon_data_size) << "\n"; |
995 | - } else if (path == "/invalid") { |
996 | - response << "HTTP/1.0 404 Not Found\r\n" |
997 | - << "Content-Length: 9\r\n" |
998 | - << "Content-Type: text/plain\r\n\r\n" |
999 | - << "not found\n"; |
1000 | - } else if (redirection.exactMatch(path)) { |
1001 | - int n = redirection.cap(1).toInt(); |
1002 | - response << "HTTP/1.0 303 See Other\r\n" |
1003 | - << "Content-Length: 9\r\n" |
1004 | - << "Content-Type: text/plain\r\n" |
1005 | - << "Location: " << baseURL(); |
1006 | - if (n == 1) { |
1007 | - response << "/" << redirection.cap(2); |
1008 | - } else { |
1009 | - response << "/redirect/" << (n - 1) << "/" << redirection.cap(2); |
1010 | - } |
1011 | - response << "\r\n\r\n" |
1012 | - << "see other\n"; |
1013 | - } |
1014 | - socket->close(); |
1015 | - } |
1016 | - |
1017 | - void discardClient() |
1018 | - { |
1019 | - QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender()); |
1020 | - if (socket) { |
1021 | - socket->deleteLater(); |
1022 | - } |
1023 | - } |
1024 | -}; |
1025 | - |
1026 | -class FaviconImageProviderTests : public QObject |
1027 | -{ |
1028 | - Q_OBJECT |
1029 | - |
1030 | -private: |
1031 | - FaviconImageProvider* provider; |
1032 | - TestHTTPServer* server; |
1033 | - |
1034 | -private Q_SLOTS: |
1035 | - void init() |
1036 | - { |
1037 | - { |
1038 | - FaviconImageProvider temp; |
1039 | - QDir(temp.cacheLocation()).removeRecursively(); |
1040 | - } |
1041 | - provider = new FaviconImageProvider; |
1042 | - server = new TestHTTPServer; |
1043 | - server->listen(); |
1044 | - } |
1045 | - |
1046 | - void cleanup() |
1047 | - { |
1048 | - delete server; |
1049 | - delete provider; |
1050 | - } |
1051 | - |
1052 | - void shouldDiscardEmptyRequests() |
1053 | - { |
1054 | - QSize size; |
1055 | - QImage icon = provider->requestImage("", &size, QSize()); |
1056 | - QVERIFY(icon.isNull()); |
1057 | - QVERIFY(!size.isValid()); |
1058 | - } |
1059 | - |
1060 | - void shouldFetchIcon() |
1061 | - { |
1062 | - QSize size; |
1063 | - QImage icon = provider->requestImage(server->baseURL() + "/favicon1.ico", &size, QSize()); |
1064 | - QVERIFY(!icon.isNull()); |
1065 | - QCOMPARE(icon.size(), QSize(1, 1)); |
1066 | - QCOMPARE(size, QSize(1, 1)); |
1067 | - QCOMPARE(server->requests.size(), 1); |
1068 | - } |
1069 | - |
1070 | - void shouldNotFetchInvalidIcon() |
1071 | - { |
1072 | - QSize size; |
1073 | - QImage icon = provider->requestImage(server->baseURL() + "/invalid", &size, QSize()); |
1074 | - QVERIFY(icon.isNull()); |
1075 | - QVERIFY(!size.isValid()); |
1076 | - QCOMPARE(server->requests.size(), 1); |
1077 | - } |
1078 | - |
1079 | - void shouldReturnCachedIcon() |
1080 | - { |
1081 | - QSize size; |
1082 | - QImage icon = provider->requestImage(server->baseURL() + "/favicon2.ico", &size, QSize()); |
1083 | - QVERIFY(!icon.isNull()); |
1084 | - QCOMPARE(server->requests.size(), 1); |
1085 | - server->requests.clear(); |
1086 | - QImage icon2 = provider->requestImage(server->baseURL() + "/favicon2.ico", &size, QSize()); |
1087 | - QVERIFY(!icon2.isNull()); |
1088 | - QVERIFY(server->requests.isEmpty()); |
1089 | - } |
1090 | - |
1091 | - void shouldHandleRedirections() |
1092 | - { |
1093 | - QSize size; |
1094 | - QImage icon = provider->requestImage(server->baseURL() + "/redirect/3/favicon3.ico", &size, QSize()); |
1095 | - QVERIFY(!icon.isNull()); |
1096 | - QCOMPARE(icon.size(), QSize(1, 1)); |
1097 | - QCOMPARE(size, QSize(1, 1)); |
1098 | - QCOMPARE(server->requests.size(), 4); |
1099 | - } |
1100 | - |
1101 | - void shouldNotHandleTooManyRedirections() |
1102 | - { |
1103 | - QSize size; |
1104 | - QImage icon = provider->requestImage(server->baseURL() + "/redirect/8/favicon4.ico", &size, QSize()); |
1105 | - QVERIFY(icon.isNull()); |
1106 | - QVERIFY(!size.isValid()); |
1107 | - QCOMPARE(server->requests.size(), 5); |
1108 | - } |
1109 | - |
1110 | - void shouldScaleIcon() |
1111 | - { |
1112 | - QSize size; |
1113 | - QImage icon = provider->requestImage(server->baseURL() + "/favicon5.ico", &size, QSize(3, 3)); |
1114 | - QVERIFY(!icon.isNull()); |
1115 | - QCOMPARE(icon.size(), QSize(3, 3)); |
1116 | - QCOMPARE(size, QSize(1, 1)); |
1117 | - } |
1118 | - |
1119 | - void shouldDiscardOldCachedIcons() |
1120 | - { |
1121 | - QSize size; |
1122 | - QImage icon = provider->requestImage(server->baseURL() + "/favicon6.ico", &size, QSize()); |
1123 | - QVERIFY(!icon.isNull()); |
1124 | - server->requests.clear(); |
1125 | - QDir cache(provider->cacheLocation(), "", QDir::Unsorted, QDir::Files | QDir::NoDotAndDotDot); |
1126 | - QCOMPARE(cache.count(), (uint) 1); |
1127 | - QString filepath = cache.filePath(cache[0]); |
1128 | - struct utimbuf ubuf; |
1129 | - ubuf.modtime = QDateTime::currentDateTime().addYears(-1).toTime_t(); |
1130 | - QCOMPARE(utime(filepath.toUtf8().constData(), &ubuf), 0); |
1131 | - icon = provider->requestImage(server->baseURL() + "/favicon6.ico", &size, QSize()); |
1132 | - QVERIFY(!icon.isNull()); |
1133 | - QCOMPARE(server->requests.size(), 1); |
1134 | - } |
1135 | -}; |
1136 | - |
1137 | -QTEST_MAIN(FaviconImageProviderTests) |
1138 | -#include "tst_FaviconImageProviderTests.moc" |
1139 | |
1140 | === modified file 'tests/unittests/qml/CMakeLists.txt' |
1141 | --- tests/unittests/qml/CMakeLists.txt 2014-08-22 13:49:15 +0000 |
1142 | +++ tests/unittests/qml/CMakeLists.txt 2014-11-27 10:18:45 +0000 |
1143 | @@ -7,8 +7,13 @@ |
1144 | endif() |
1145 | |
1146 | set(TEST tst_QmlTests) |
1147 | -add_executable(${TEST} tst_QmlTests.cpp) |
1148 | -qt5_use_modules(${TEST} QuickTest) |
1149 | +set(SOURCES |
1150 | + ${webbrowser-common_SOURCE_DIR}/favicon-fetcher.cpp |
1151 | + tst_QmlTests.cpp |
1152 | +) |
1153 | +add_executable(${TEST} ${SOURCES}) |
1154 | +qt5_use_modules(${TEST} Qml QuickTest) |
1155 | +include_directories(${webbrowser-common_SOURCE_DIR}) |
1156 | add_test(${TEST} ${XVFB_COMMAND} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -import ${CMAKE_BINARY_DIR}/src) |
1157 | |
1158 | # copy qml files under test to build dir |
1159 | |
1160 | === modified file 'tests/unittests/qml/tst_QmlTests.cpp' |
1161 | --- tests/unittests/qml/tst_QmlTests.cpp 2014-08-22 13:49:15 +0000 |
1162 | +++ tests/unittests/qml/tst_QmlTests.cpp 2014-11-27 10:18:45 +0000 |
1163 | @@ -1,5 +1,5 @@ |
1164 | /* |
1165 | - * Copyright 2013 Canonical Ltd. |
1166 | + * Copyright 2013-2014 Canonical Ltd. |
1167 | * |
1168 | * This file is part of webbrowser-app. |
1169 | * |
1170 | @@ -16,6 +16,17 @@ |
1171 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1172 | */ |
1173 | |
1174 | +// Qt |
1175 | +#include <QtQml/QtQml> |
1176 | #include <QtQuickTest/QtQuickTest> |
1177 | -QUICK_TEST_MAIN(QmlTests) |
1178 | - |
1179 | + |
1180 | +// local |
1181 | +#include "favicon-fetcher.h" |
1182 | + |
1183 | +int main(int argc, char** argv) |
1184 | +{ |
1185 | + const char* uri = "webbrowsercommon.private"; |
1186 | + qmlRegisterType<FaviconFetcher>(uri, 0, 1, "FaviconFetcher"); |
1187 | + |
1188 | + return quick_test_main(argc, argv, "QmlTests", 0); |
1189 | +} |
we'll need to add an autopilot test (or qml test if that make sense) for this before it will be acceptable for rtm