summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Varga <pvarga@inf.u-szeged.hu>2015-12-01 10:50:02 +0100
committerPeter Varga <pvarga@inf.u-szeged.hu>2016-02-03 16:21:12 +0000
commit41d69eb0fa2375f0da6ba9b35136f5598be4b3a4 (patch)
tree3c754447cf5d1c854eb1bc7fdbe3d6777fcdac0c
parent4713387c052d54e0f5ea02efaeaa25931d1cd7ee (diff)
Add FaviconManager to core
The new icon manager uses the WebContents::DownloadImage() API for downloading icons. It proposes the best quality among the available favicons via the iconChanged signal. Change-Id: I66a014365b6f6560ff34d40ee870aee84e4e70e4 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@theqtcompany.com>
-rw-r--r--src/core/core_gyp_generator.pro3
-rw-r--r--src/core/favicon_manager.cpp390
-rw-r--r--src/core/favicon_manager.h120
-rw-r--r--src/core/favicon_manager_p.h106
-rw-r--r--src/core/type_conversion.cpp45
-rw-r--r--src/core/type_conversion.h6
-rw-r--r--src/core/web_contents_adapter.cpp6
-rw-r--r--src/core/web_contents_adapter.h2
-rw-r--r--src/core/web_contents_delegate_qt.cpp47
-rw-r--r--src/core/web_contents_delegate_qt.h3
-rw-r--r--tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp3
-rw-r--r--tests/auto/quick/qmltests/data/favicon-misc.html11
-rw-r--r--tests/auto/quick/qmltests/data/favicon-shortcut.html10
-rw-r--r--tests/auto/quick/qmltests/data/favicon-single.html9
-rw-r--r--tests/auto/quick/qmltests/data/favicon-touch.html10
-rw-r--r--tests/auto/quick/qmltests/data/favicon-unavailable.html9
-rw-r--r--tests/auto/quick/qmltests/data/favicon.html4
-rw-r--r--tests/auto/quick/qmltests/data/favicon2.html4
-rw-r--r--tests/auto/quick/qmltests/data/icons/favicon.png (renamed from tests/auto/quick/qmltests/data/favicon.png)bin3961 -> 3961 bytes
-rw-r--r--tests/auto/quick/qmltests/data/icons/qt144.pngbin0 -> 8315 bytes
-rw-r--r--tests/auto/quick/qmltests/data/icons/qt32.icobin0 -> 4286 bytes
-rw-r--r--tests/auto/quick/qmltests/data/icons/small-favicon.png (renamed from tests/auto/quick/qmltests/data/small-favicon.png)bin891 -> 891 bytes
-rw-r--r--tests/auto/quick/qmltests/data/tst_favicon.qml166
-rw-r--r--tests/auto/quick/qmltests/data/tst_faviconImage.qml (renamed from tests/auto/quick/qmltests/data/tst_favIconLoad.qml)81
-rw-r--r--tests/auto/quick/qmltests/qmltests.pro13
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/qwebenginefaviconmanager.pro2
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-misc.html11
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-shortcut.html10
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-single.html9
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-touch.html10
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-unavailable.html9
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt144.pngbin0 -> 8315 bytes
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt32.icobin0 -> 4286 bytes
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/resources/test1.html1
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.cpp279
-rw-r--r--tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.qrc12
-rw-r--r--tests/auto/widgets/widgets.pro1
37 files changed, 1346 insertions, 46 deletions
diff --git a/src/core/core_gyp_generator.pro b/src/core/core_gyp_generator.pro
index 80bf662d6..5e6641d51 100644
--- a/src/core/core_gyp_generator.pro
+++ b/src/core/core_gyp_generator.pro
@@ -53,6 +53,7 @@ SOURCES = \
desktop_screen_qt.cpp \
dev_tools_http_handler_delegate_qt.cpp \
download_manager_delegate_qt.cpp \
+ favicon_manager.cpp \
file_picker_controller.cpp \
gl_context_qt.cpp \
gl_surface_qt.cpp \
@@ -130,6 +131,8 @@ HEADERS = \
dev_tools_http_handler_delegate_qt.h \
download_manager_delegate_qt.h \
chromium_gpu_helper.h \
+ favicon_manager_p.h \
+ favicon_manager.h \
file_picker_controller.h \
gl_context_qt.h \
gl_surface_qt.h \
diff --git a/src/core/favicon_manager.cpp b/src/core/favicon_manager.cpp
new file mode 100644
index 000000000..f3260b3c4
--- /dev/null
+++ b/src/core/favicon_manager.cpp
@@ -0,0 +1,390 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "favicon_manager.h"
+#include "favicon_manager_p.h"
+
+#include "type_conversion.h"
+#include "web_contents_adapter_client.h"
+
+#include "base/bind.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkPixelRef.h"
+#include "ui/gfx/geometry/size.h"
+
+#include <QtCore/QUrl>
+#include <QtGui/QIcon>
+
+namespace QtWebEngineCore {
+
+static inline bool isResourceUrl(const QUrl &url)
+{
+ return !url.scheme().compare(QLatin1String("qrc"));
+}
+
+static inline unsigned area(const QSize &size)
+{
+ return size.width() * size.height();
+}
+
+
+FaviconManagerPrivate::FaviconManagerPrivate(content::WebContents *webContents, WebContentsAdapterClient *viewClient)
+ : m_webContents(webContents)
+ , m_viewClient(viewClient)
+ , m_weakFactory(this)
+{
+}
+
+FaviconManagerPrivate::~FaviconManagerPrivate()
+{
+}
+
+int FaviconManagerPrivate::downloadIcon(const QUrl &url, bool candidate)
+{
+ static int fakeId = 0;
+ int id;
+
+ bool cached = candidate && m_icons.contains(url);
+ if (isResourceUrl(url) || cached) {
+ id = --fakeId;
+ m_pendingRequests.insert(id, url);
+ } else {
+ id = m_webContents->DownloadImage(
+ toGurl(url),
+ true, // is_favicon
+ 0, // no max size
+ false, // normal cache policy
+ base::Bind(&FaviconManagerPrivate::iconDownloadFinished, m_weakFactory.GetWeakPtr()));
+ }
+
+ if (candidate) {
+ Q_ASSERT(!m_inProgressCandidateRequests.contains(id));
+ m_inProgressCandidateRequests.insert(id, url);
+ } else {
+ Q_ASSERT(!m_inProgressCustomRequests.contains(id));
+ m_inProgressCustomRequests.insert(id, url);
+ }
+
+ return id;
+}
+
+void FaviconManagerPrivate::iconDownloadFinished(int id,
+ int status,
+ const GURL &url,
+ const std::vector<SkBitmap> &bitmaps,
+ const std::vector<gfx::Size> &original_bitmap_sizes)
+{
+ Q_UNUSED(status);
+ Q_UNUSED(url);
+ Q_UNUSED(original_bitmap_sizes);
+
+ storeIcon(id, toQIcon(bitmaps));
+}
+
+/* Pending requests are used as a workaround for avoiding signal iconChanged when
+ * accessing each cached icons or icons stored in qrc. They don't have to be downloaded
+ * thus the m_inProgressCustomRequests maybe emptied right before the next icon is added to
+ * in-progress-requests queue. The m_pendingRequests stores these requests until all
+ * candidates are added to the queue then pending requests should be cleaned up by this
+ * function.
+ */
+void FaviconManagerPrivate::downloadPendingRequests()
+{
+ Q_FOREACH (int id, m_pendingRequests.keys()) {
+ QIcon icon;
+
+ QUrl requestUrl = m_pendingRequests[id];
+ if (isResourceUrl(requestUrl) && !m_icons.contains(requestUrl))
+ icon = QIcon(requestUrl.toString().remove(0, 3));
+
+ storeIcon(id, icon);
+ }
+
+ m_pendingRequests.clear();
+}
+
+void FaviconManagerPrivate::storeIcon(int id, const QIcon &icon)
+{
+ Q_Q(FaviconManager);
+
+ bool candidate = m_inProgressCandidateRequests.contains(id);
+
+ QUrl requestUrl = candidate ? m_inProgressCandidateRequests[id] : m_inProgressCustomRequests[id];
+ FaviconInfo &faviconInfo = q->m_faviconInfoMap[requestUrl];
+
+ unsigned iconCount = 0;
+ if (!icon.isNull())
+ iconCount = icon.availableSizes().count();
+
+ if (iconCount > 0) {
+ m_icons.insert(requestUrl, icon);
+
+ faviconInfo.size = icon.availableSizes().at(0);
+ if (iconCount > 1) {
+ faviconInfo.multiSize = true;
+ unsigned bestArea = area(faviconInfo.size);
+ for (unsigned i = 1; i < iconCount; ++i) {
+ QSize iconSize = icon.availableSizes().at(i);
+ if (bestArea < area(iconSize)) {
+ faviconInfo.size = iconSize;
+ bestArea = area(iconSize);
+ }
+ }
+ }
+
+ q->m_hasDownloadedIcon = true;
+ } else if (id < 0) {
+ // Icon is cached
+ q->m_hasDownloadedIcon = true;
+ } else {
+ // Reset size if icon cannot be downloaded
+ faviconInfo.size = QSize(0, 0);
+ }
+
+ if (candidate) {
+ m_inProgressCandidateRequests.remove(id);
+ if (m_inProgressCandidateRequests.isEmpty())
+ m_viewClient->iconChanged(q->getProposedFaviconInfo().url);
+ } else {
+ m_inProgressCustomRequests.remove(id);
+ }
+
+ Q_EMIT q->iconDownloaded(requestUrl);
+}
+
+FaviconManager::FaviconManager(FaviconManagerPrivate *d)
+ : m_hasDownloadedIcon(false)
+{
+ Q_ASSERT(d);
+ d_ptr.reset(d);
+
+ d->q_ptr = this;
+}
+
+FaviconManager::~FaviconManager()
+{
+}
+
+QIcon FaviconManager::getIcon(const QUrl &url) const
+{
+ Q_D(const FaviconManager);
+ return d->m_icons[url];
+}
+
+FaviconInfo FaviconManager::getFaviconInfo(const QUrl &url) const
+{
+ return m_faviconInfoMap[url];
+}
+
+QList<FaviconInfo> FaviconManager::getFaviconInfoList(bool candidatesOnly) const
+{
+ QList<FaviconInfo> faviconInfoList = m_faviconInfoMap.values();
+
+ if (candidatesOnly) {
+ QMutableListIterator<FaviconInfo> it(faviconInfoList);
+ while (it.hasNext()) {
+ if (!it.next().candidate)
+ it.remove();
+ }
+ }
+
+ return faviconInfoList;
+}
+
+
+void FaviconManager::downloadIcon(const QUrl &url, FaviconInfo::FaviconType iconType)
+{
+ Q_D(FaviconManager);
+
+ // If the favicon cannot be found in the list that means that it is not a candidate
+ // for any visited page (including the current one). In this case the type of the icon
+ // is unknown: it has to be specified explicitly.
+ if (!m_faviconInfoMap.contains(url)) {
+ FaviconInfo newFaviconInfo(url, iconType);
+ m_faviconInfoMap.insert(url, newFaviconInfo);
+ }
+
+ d->downloadIcon(url, false);
+ d->downloadPendingRequests();
+}
+
+void FaviconManager::removeIcon(const QUrl &url)
+{
+ Q_D(FaviconManager);
+ int removed = d->m_icons.remove(url);
+
+ if (removed) {
+ Q_ASSERT(removed == 1);
+ Q_ASSERT(m_faviconInfoMap.contains(url));
+ m_faviconInfoMap[url].size = QSize(0, 0);
+ }
+}
+
+bool FaviconManager::hasAvailableCandidateIcon() const
+{
+ Q_D(const FaviconManager);
+ return m_hasDownloadedIcon || !d->m_inProgressCandidateRequests.isEmpty();
+}
+
+void FaviconManager::update(QList<FaviconInfo> &candidates)
+{
+ Q_D(FaviconManager);
+ updateCandidates(candidates);
+
+ Q_FOREACH (FaviconInfo faviconInfo, m_faviconInfoMap.values()) {
+ if (!faviconInfo.candidate || faviconInfo.type != FaviconInfo::Favicon)
+ continue;
+
+ if (faviconInfo.isValid())
+ d->downloadIcon(faviconInfo.url, true);
+ }
+
+ d->downloadPendingRequests();
+}
+
+void FaviconManager::updateCandidates(QList<FaviconInfo> &candidates)
+{
+ Q_FOREACH (FaviconInfo candidateFaviconInfo, candidates) {
+ QUrl candidateUrl = candidateFaviconInfo.url;
+ if (m_faviconInfoMap.contains(candidateUrl)) {
+ m_faviconInfoMap[candidateUrl].candidate = true;
+ // Update type in case of the icon was downloaded manually
+ m_faviconInfoMap[candidateUrl].type = candidateFaviconInfo.type;
+ continue;
+ }
+
+ candidateFaviconInfo.candidate = true;
+ m_faviconInfoMap.insert(candidateUrl, candidateFaviconInfo);
+ }
+}
+
+void FaviconManager::resetCandidates()
+{
+ m_hasDownloadedIcon = false;
+ Q_FOREACH (const QUrl key, m_faviconInfoMap.keys())
+ m_faviconInfoMap[key].candidate = false;
+}
+
+
+FaviconInfo FaviconManager::getProposedFaviconInfo() const
+{
+ FaviconInfo proposedFaviconInfo = getFirstFaviconInfo();
+
+ // If nothing has been downloaded yet return the first favicon
+ // if there is available for dev-tools
+ if (!m_hasDownloadedIcon)
+ return proposedFaviconInfo;
+
+ unsigned bestArea = area(proposedFaviconInfo.size);
+ Q_FOREACH (const FaviconInfo faviconInfo, m_faviconInfoMap.values()) {
+ if (!faviconInfo.candidate || faviconInfo.type != FaviconInfo::Favicon)
+ continue;
+
+ if (faviconInfo.isValid() && bestArea < area(faviconInfo.size)) {
+ proposedFaviconInfo = faviconInfo;
+ bestArea = area(proposedFaviconInfo.size);
+ }
+ }
+
+ return proposedFaviconInfo;
+}
+
+FaviconInfo FaviconManager::getFirstFaviconInfo() const
+{
+ Q_FOREACH (const FaviconInfo faviconInfo, m_faviconInfoMap.values()) {
+ if (!faviconInfo.candidate || faviconInfo.type != FaviconInfo::Favicon)
+ continue;
+
+ if (faviconInfo.isValid())
+ return faviconInfo;
+ }
+
+ return FaviconInfo();
+}
+
+
+
+FaviconInfo::FaviconInfo()
+ : url(QUrl())
+ , type(FaviconInfo::InvalidIcon)
+ , size(QSize(0, 0))
+ , candidate(false)
+ , multiSize(false)
+{
+}
+
+FaviconInfo::FaviconInfo(const FaviconInfo &other)
+ : url(other.url)
+ , type(other.type)
+ , size(other.size)
+ , candidate(other.candidate)
+{
+}
+
+FaviconInfo::FaviconInfo(const QUrl &url, FaviconInfo::FaviconType type)
+ : url(url)
+ , type(type)
+ , size(QSize(0, 0))
+ , candidate(false)
+ , multiSize(false)
+{
+}
+
+FaviconInfo::~FaviconInfo()
+{
+}
+
+bool FaviconInfo::isValid() const
+{
+ if (type == FaviconInfo::InvalidIcon)
+ return false;
+
+ if (url.isEmpty() || !url.isValid())
+ return false;
+
+ return true;
+}
+
+bool FaviconInfo::isDownloaded() const
+{
+ return area(size) > 0;
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/favicon_manager.h b/src/core/favicon_manager.h
new file mode 100644
index 000000000..eaae8123f
--- /dev/null
+++ b/src/core/favicon_manager.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FAVICON_MANAGER_H
+#define FAVICON_MANAGER_H
+
+#include "qtwebenginecoreglobal.h"
+
+#include <QtCore/QMap>
+#include <QtCore/QObject>
+#include <QtCore/QSize>
+#include <QtCore/QUrl>
+
+#include "web_engine_settings.h"
+
+namespace QtWebEngineCore {
+
+class FaviconManagerPrivate;
+
+// Based on src/3rdparty/chromium/content/public/common/favicon_url.h
+class QWEBENGINE_EXPORT FaviconInfo {
+public:
+ enum FaviconType {
+ InvalidIcon,
+ Favicon,
+ TouchIcon,
+ TouchPrecomposedIcon
+ };
+
+ FaviconInfo();
+ FaviconInfo(const FaviconInfo &);
+ FaviconInfo(const QUrl &, FaviconInfo::FaviconType);
+ ~FaviconInfo();
+
+ bool isValid() const;
+ bool isDownloaded() const;
+
+ QUrl url;
+ FaviconType type;
+ // Stores the size of the highest quality in case of multi-size icon
+ QSize size;
+ bool candidate;
+ bool multiSize;
+};
+
+
+class QWEBENGINE_EXPORT FaviconManager : public QObject {
+ Q_OBJECT
+public:
+ ~FaviconManager();
+
+ QIcon getIcon(const QUrl &) const;
+ FaviconInfo getFaviconInfo(const QUrl &) const;
+ QList<FaviconInfo> getFaviconInfoList(bool) const;
+ void downloadIcon(const QUrl &url, FaviconInfo::FaviconType iconType = FaviconInfo::Favicon);
+ void removeIcon(const QUrl &);
+
+Q_SIGNALS:
+ void iconDownloaded(const QUrl &url);
+
+private:
+ FaviconManager(FaviconManagerPrivate *);
+
+ bool hasAvailableCandidateIcon() const;
+ void update(QList<FaviconInfo> &);
+ void updateCandidates(QList<FaviconInfo> &);
+ void resetCandidates();
+
+ FaviconInfo getProposedFaviconInfo() const;
+ FaviconInfo getFirstFaviconInfo() const;
+
+ QMap<QUrl, FaviconInfo> m_faviconInfoMap;
+ bool m_hasDownloadedIcon;
+
+ Q_DISABLE_COPY(FaviconManager)
+ Q_DECLARE_PRIVATE(FaviconManager)
+ QScopedPointer<FaviconManagerPrivate> d_ptr;
+
+ friend class WebContentsDelegateQt;
+};
+
+} // namespace QtWebEngineCore
+
+#endif // FAVICON_MANAGER_H
diff --git a/src/core/favicon_manager_p.h b/src/core/favicon_manager_p.h
new file mode 100644
index 000000000..8358245a2
--- /dev/null
+++ b/src/core/favicon_manager_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FAVICON_MANAGER_P_H
+#define FAVICON_MANAGER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtwebenginecoreglobal_p.h"
+
+#include <QtCore/QMap>
+#include <QtCore/QObject>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+
+QT_FORWARD_DECLARE_CLASS(QUrl)
+
+class GURL;
+class SkBitmap;
+
+namespace gfx {
+ class Size;
+}
+
+namespace content {
+ class WebContents;
+}
+
+namespace QtWebEngineCore {
+
+class FaviconManager;
+class WebContentsAdapterClient;
+
+class FaviconManagerPrivate {
+public:
+ FaviconManagerPrivate(content::WebContents *, WebContentsAdapterClient *);
+ ~FaviconManagerPrivate();
+
+ int downloadIcon(const QUrl &, bool);
+
+ void iconDownloadFinished(int, int, const GURL &, const std::vector<SkBitmap> &, const std::vector<gfx::Size> &);
+ void storeIcon(int, const QIcon &);
+ void downloadPendingRequests();
+
+ content::WebContents *m_webContents;
+ WebContentsAdapterClient *m_viewClient;
+ base::WeakPtrFactory<FaviconManagerPrivate> m_weakFactory;
+
+ QMap<QUrl, QIcon> m_icons;
+ QMap<int, QUrl> m_inProgressCandidateRequests;
+ QMap<int, QUrl> m_inProgressCustomRequests;
+ QMap<int, QUrl> m_pendingRequests;
+
+ Q_DECLARE_PUBLIC(FaviconManager)
+ FaviconManager *q_ptr;
+};
+
+} // namespace QtWebEngineCore
+
+#endif // FAVICON_MANAGER_P_H
diff --git a/src/core/type_conversion.cpp b/src/core/type_conversion.cpp
index ac458df31..ccfc2fdc6 100644
--- a/src/core/type_conversion.cpp
+++ b/src/core/type_conversion.cpp
@@ -128,6 +128,23 @@ QImage toQImage(const gfx::ImageSkiaRep &imageSkiaRep)
return image;
}
+QIcon toQIcon(const std::vector<SkBitmap> &bitmaps)
+{
+ if (!bitmaps.size())
+ return QIcon();
+
+ QIcon icon;
+
+ for (unsigned i = 0; i < bitmaps.size(); ++i) {
+ SkBitmap bitmap = bitmaps[i];
+ QImage image = toQImage(bitmap);
+
+ icon.addPixmap(QPixmap::fromImage(image).copy());
+ }
+
+ return icon;
+}
+
int flagsFromModifiers(Qt::KeyboardModifiers modifiers)
{
int modifierFlags = ui::EF_NONE;
@@ -152,5 +169,33 @@ int flagsFromModifiers(Qt::KeyboardModifiers modifiers)
return modifierFlags;
}
+FaviconInfo toFaviconInfo(const content::FaviconURL &favicon_url)
+{
+ FaviconInfo info;
+
+ info.url = toQt(favicon_url.icon_url);
+
+ switch (favicon_url.icon_type) {
+ case content::FaviconURL::FAVICON:
+ info.type = FaviconInfo::Favicon;
+ break;
+ case content::FaviconURL::TOUCH_ICON:
+ info.type = FaviconInfo::TouchIcon;
+ break;
+ case content::FaviconURL::TOUCH_PRECOMPOSED_ICON:
+ info.type = FaviconInfo::TouchPrecomposedIcon;
+ break;
+ default:
+ info.type = FaviconInfo::InvalidIcon;
+ break;
+ }
+
+ // TODO: Add support for rel sizes attribute (favicon_url.icon_sizes):
+ // http://www.w3schools.com/tags/att_link_sizes.asp
+ info.size = QSize(0, 0);
+
+ return info;
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h
index 0f3357948..8789cf2b7 100644
--- a/src/core/type_conversion.h
+++ b/src/core/type_conversion.h
@@ -43,6 +43,7 @@
#include <QColor>
#include <QDateTime>
#include <QDir>
+#include <QIcon>
#include <QImage>
#include <QMatrix4x4>
#include <QNetworkCookie>
@@ -53,6 +54,8 @@
#include "base/files/file_path.h"
#include "base/time/time.h"
#include "content/public/common/file_chooser_file_info.h"
+#include "content/public/common/favicon_url.h"
+#include "favicon_manager.h"
#include "net/cookies/canonical_cookie.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
@@ -164,6 +167,7 @@ inline QImage toQImage(const SkBitmap &bitmap, QImage::Format format)
QImage toQImage(const SkBitmap &bitmap);
QImage toQImage(const gfx::ImageSkiaRep &imageSkiaRep);
+QIcon toQIcon(const std::vector<SkBitmap> &bitmaps);
inline QMatrix4x4 toQt(const SkMatrix44 &m)
{
@@ -261,6 +265,8 @@ inline QStringList fromVector(const std::vector<base::string16> &vector)
}
#endif
+FaviconInfo toFaviconInfo(const content::FaviconURL &);
+
} // namespace QtWebEngineCore
#endif // TYPE_CONVERSION_H
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index bf36a771c..eb4436018 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -1207,4 +1207,10 @@ WebContentsAdapterClient::renderProcessExitStatus(int terminationStatus) {
return status;
}
+FaviconManager *WebContentsAdapter::faviconManager()
+{
+ Q_D(WebContentsAdapter);
+ return d->webContentsDelegate->faviconManager();
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index e6aef23ec..c7c2c1edf 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -65,6 +65,7 @@ namespace QtWebEngineCore {
class BrowserContextQt;
class MessagePassingInterface;
class WebContentsAdapterPrivate;
+class FaviconManager;
class QWEBENGINE_EXPORT WebContentsAdapter : public QSharedData {
public:
@@ -154,6 +155,7 @@ public:
BrowserContextAdapter* browserContextAdapter();
QWebChannel *webChannel() const;
void setWebChannel(QWebChannel *, uint worldId);
+ FaviconManager *faviconManager();
QPointF lastScrollOffset() const;
QSizeF lastContentsSize() const;
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index 95066a065..a3d2d816e 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -46,6 +46,8 @@
#include "browser_context_adapter.h"
#include "color_chooser_qt.h"
#include "color_chooser_controller.h"
+#include "favicon_manager.h"
+#include "favicon_manager_p.h"
#include "file_picker_controller.h"
#include "media_capture_devices_dispatcher.h"
#include "network_delegate_qt.h"
@@ -89,6 +91,7 @@ static WebContentsAdapterClient::JavaScriptConsoleMessageLevel mapToJavascriptCo
WebContentsDelegateQt::WebContentsDelegateQt(content::WebContents *webContents, WebContentsAdapterClient *adapterClient)
: m_viewClient(adapterClient)
, m_lastReceivedFindReply(0)
+ , m_faviconManager(new FaviconManager(new FaviconManagerPrivate(webContents, adapterClient)))
{
webContents->SetDelegate(this);
Observe(webContents);
@@ -175,8 +178,10 @@ void WebContentsDelegateQt::DidStartProvisionalLoadForFrame(content::RenderFrame
m_loadingErrorFrameList.append(render_frame_host->GetRoutingID());
// Trigger LoadStarted signal for main frame's error page only.
- if (!render_frame_host->GetParent())
+ if (!render_frame_host->GetParent()) {
+ m_faviconManager->resetCandidates();
m_viewClient->loadStarted(toQt(validated_url), true);
+ }
return;
}
@@ -185,6 +190,7 @@ void WebContentsDelegateQt::DidStartProvisionalLoadForFrame(content::RenderFrame
return;
m_loadingErrorFrameList.clear();
+ m_faviconManager->resetCandidates();
m_viewClient->loadStarted(toQt(validated_url));
}
@@ -208,6 +214,7 @@ void WebContentsDelegateQt::DidFailLoad(content::RenderFrameHost* render_frame_h
if (m_loadingErrorFrameList.removeOne(render_frame_host->GetRoutingID()) || render_frame_host->GetParent())
return;
+ m_viewClient->iconChanged(QUrl());
m_viewClient->loadFinished(false /* success */ , toQt(validated_url), false /* isErrorPage */, error_code, toQt(error_description));
m_viewClient->loadProgressChanged(0);
}
@@ -230,28 +237,31 @@ void WebContentsDelegateQt::DidFinishLoad(content::RenderFrameHost* render_frame
m_viewClient->loadFinished(true, toQt(validated_url));
- content::NavigationEntry *entry = web_contents()->GetController().GetActiveEntry();
+ content::NavigationEntry *entry = web_contents()->GetController().GetVisibleEntry();
if (!entry)
return;
- content::FaviconStatus &favicon = entry->GetFavicon();
- if (favicon.valid)
- m_viewClient->iconChanged(toQt(favicon.url));
- else
+
+ // No available icon for the current entry
+ if (!entry->GetFavicon().valid && !m_faviconManager->hasAvailableCandidateIcon())
m_viewClient->iconChanged(QUrl());
}
-void WebContentsDelegateQt::DidUpdateFaviconURL(const std::vector<content::FaviconURL>& candidates)
+void WebContentsDelegateQt::DidUpdateFaviconURL(const std::vector<content::FaviconURL> &candidates)
{
+ QList<FaviconInfo> faviconCandidates;
Q_FOREACH (content::FaviconURL candidate, candidates) {
- if (candidate.icon_type == content::FaviconURL::FAVICON && !candidate.icon_url.is_empty()) {
- content::NavigationEntry *entry = web_contents()->GetController().GetActiveEntry();
- if (!entry)
- continue;
- content::FaviconStatus &favicon = entry->GetFavicon();
- favicon.url = candidate.icon_url;
- favicon.valid = toQt(candidate.icon_url).isValid();
- break;
- }
+ // Store invalid candidates too for later debugging via API
+ faviconCandidates.append(toFaviconInfo(candidate));
+ }
+
+ m_faviconManager->update(faviconCandidates);
+
+ content::NavigationEntry *entry = web_contents()->GetController().GetVisibleEntry();
+ if (entry) {
+ FaviconInfo proposedFaviconInfo = m_faviconManager->getProposedFaviconInfo();
+ content::FaviconStatus &favicon = entry->GetFavicon();
+ favicon.url = toGurl(proposedFaviconInfo.url);
+ favicon.valid = proposedFaviconInfo.isValid();
}
}
@@ -426,4 +436,9 @@ void WebContentsDelegateQt::BeforeUnloadFired(content::WebContents *tab, bool pr
m_viewClient->windowCloseRejected();
}
+FaviconManager *WebContentsDelegateQt::faviconManager()
+{
+ return m_faviconManager.data();
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h
index d646a1322..afa4030d0 100644
--- a/src/core/web_contents_delegate_qt.h
+++ b/src/core/web_contents_delegate_qt.h
@@ -48,6 +48,7 @@
#include "base/callback.h"
#include "color_chooser_controller.h"
+#include "favicon_manager.h"
#include "javascript_dialog_manager_qt.h"
#include <QtCore/qvector.h>
#include <QtCore/qcompilerdetection.h>
@@ -119,6 +120,7 @@ public:
void allowCertificateError(const QSharedPointer<CertificateErrorController> &) ;
void requestGeolocationPermission(const QUrl &requestingOrigin);
void launchExternalURL(const QUrl &url, ui::PageTransition page_transition, bool is_main_frame);
+ FaviconManager *faviconManager();
private:
WebContentsAdapter *createWindow(content::WebContents *new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture);
@@ -127,6 +129,7 @@ private:
QString m_lastSearchedString;
int m_lastReceivedFindReply;
QVector<int64> m_loadingErrorFrameList;
+ QScopedPointer<FaviconManager> m_faviconManager;
};
} // namespace QtWebEngineCore
diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
index a65ffb868..180953ed4 100644
--- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
+++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
@@ -150,6 +150,9 @@ public:
void interceptRequest(QWebEngineUrlRequestInfo &info) override
{
+ if (info.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeFavicon)
+ return;
+
requestedUrls.append(info.requestUrl());
info.redirect(QUrl("data:text/html,<p>hello"));
}
diff --git a/tests/auto/quick/qmltests/data/favicon-misc.html b/tests/auto/quick/qmltests/data/favicon-misc.html
new file mode 100644
index 000000000..9e788bdf4
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/favicon-misc.html
@@ -0,0 +1,11 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="shortcut icon" href="icons/qt32.ico" />
+ <link rel="apple-touch-icon" href="icons/qt144.png" />
+ <link rel="shortcut icon" href="icons/unavailable.ico" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/quick/qmltests/data/favicon-shortcut.html b/tests/auto/quick/qmltests/data/favicon-shortcut.html
new file mode 100644
index 000000000..786cdb816
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/favicon-shortcut.html
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="shortcut icon" href="icons/qt32.ico" />
+ <link rel="shortcut icon" href="icons/qt144.png" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/quick/qmltests/data/favicon-single.html b/tests/auto/quick/qmltests/data/favicon-single.html
new file mode 100644
index 000000000..eb4675c75
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/favicon-single.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="shortcut icon" href="icons/qt32.ico" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/quick/qmltests/data/favicon-touch.html b/tests/auto/quick/qmltests/data/favicon-touch.html
new file mode 100644
index 000000000..271783434
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/favicon-touch.html
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="apple-touch-icon" href="icons/qt32.ico" />
+ <link rel="apple-touch-icon" href="icons/qt144.png" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/quick/qmltests/data/favicon-unavailable.html b/tests/auto/quick/qmltests/data/favicon-unavailable.html
new file mode 100644
index 000000000..c45664294
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/favicon-unavailable.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="shortcut icon" href="icons/unavailable.ico" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/quick/qmltests/data/favicon.html b/tests/auto/quick/qmltests/data/favicon.html
index c9f225c52..9823fa323 100644
--- a/tests/auto/quick/qmltests/data/favicon.html
+++ b/tests/auto/quick/qmltests/data/favicon.html
@@ -1,10 +1,10 @@
<html>
<head>
</head>
-<link type="image/png" href="favicon.png" sizes="48x48" rel="icon" />
+<link type="image/png" href="icons/favicon.png" sizes="48x48" rel="icon" />
<body>
<p>It's expected that you see a favicon displayed for this page when you open it as a local file.</p>
<p>The favicon looks like this:</p>
-<img src="favicon.png"/>
+<img src="icons/favicon.png"/>
</body>
</html>
diff --git a/tests/auto/quick/qmltests/data/favicon2.html b/tests/auto/quick/qmltests/data/favicon2.html
index 5548b867f..81c2690fe 100644
--- a/tests/auto/quick/qmltests/data/favicon2.html
+++ b/tests/auto/quick/qmltests/data/favicon2.html
@@ -1,10 +1,10 @@
<html>
<head>
</head>
-<link type="image/png" href="small-favicon.png" sizes="16x16" rel="icon" />
+<link type="image/png" href="icons/small-favicon.png" sizes="16x16" rel="icon" />
<body>
<p>It's expected that you see a favicon displayed for this page when you open it as a local file.</p>
<p>The favicon looks like this:</p>
-<img src="small-favicon.png"/>
+<img src="icons/small-favicon.png"/>
</body>
</html>
diff --git a/tests/auto/quick/qmltests/data/favicon.png b/tests/auto/quick/qmltests/data/icons/favicon.png
index 35717cca5..35717cca5 100644
--- a/tests/auto/quick/qmltests/data/favicon.png
+++ b/tests/auto/quick/qmltests/data/icons/favicon.png
Binary files differ
diff --git a/tests/auto/quick/qmltests/data/icons/qt144.png b/tests/auto/quick/qmltests/data/icons/qt144.png
new file mode 100644
index 000000000..050b1e066
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/icons/qt144.png
Binary files differ
diff --git a/tests/auto/quick/qmltests/data/icons/qt32.ico b/tests/auto/quick/qmltests/data/icons/qt32.ico
new file mode 100644
index 000000000..2f6fcb5bc
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/icons/qt32.ico
Binary files differ
diff --git a/tests/auto/quick/qmltests/data/small-favicon.png b/tests/auto/quick/qmltests/data/icons/small-favicon.png
index 4462752a5..4462752a5 100644
--- a/tests/auto/quick/qmltests/data/small-favicon.png
+++ b/tests/auto/quick/qmltests/data/icons/small-favicon.png
Binary files differ
diff --git a/tests/auto/quick/qmltests/data/tst_favicon.qml b/tests/auto/quick/qmltests/data/tst_favicon.qml
new file mode 100644
index 000000000..fab2e9755
--- /dev/null
+++ b/tests/auto/quick/qmltests/data/tst_favicon.qml
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtTest 1.0
+import QtWebEngine 1.3
+import QtWebEngine.testsupport 1.0
+
+TestWebEngineView {
+ id: webEngineView
+ width: 200
+ height: 400
+
+ testSupport: WebEngineTestSupport {
+ property var errorPageLoadStatus: null
+
+ function waitForErrorPageLoadSucceeded() {
+ var success = _waitFor(function() { return testSupport.errorPageLoadStatus == WebEngineView.LoadSucceededStatus })
+ testSupport.errorPageLoadStatus = null
+ return success
+ }
+
+ errorPage.onLoadingChanged: {
+ errorPageLoadStatus = loadRequest.status
+ }
+ }
+
+ SignalSpy {
+ id: iconChangedSpy
+ target: webEngineView
+ signalName: "iconChanged"
+ }
+
+ TestCase {
+ id: test
+ name: "WebEngineFavicon"
+ when: windowShown
+
+ function init() {
+ if (webEngineView.icon != '') {
+ // If this is not the first test, then load a blank page without favicon, restoring the initial state.
+ webEngineView.url = 'about:blank'
+ verify(webEngineView.waitForLoadSucceeded())
+ iconChangedSpy.wait()
+ }
+
+ iconChangedSpy.clear()
+ }
+
+ function test_noFavicon() {
+ compare(iconChangedSpy.count, 0)
+
+ var url = Qt.resolvedUrl("test1.html")
+ webEngineView.url = url
+ verify(webEngineView.waitForLoadSucceeded())
+
+ iconChangedSpy.wait()
+ compare(iconChangedSpy.count, 1)
+
+ var iconUrl = webEngineView.icon
+ compare(iconUrl, Qt.resolvedUrl(""))
+ }
+
+ function test_aboutBlank() {
+ compare(iconChangedSpy.count, 0)
+
+ var url = Qt.resolvedUrl("about:blank")
+ webEngineView.url = url
+ verify(webEngineView.waitForLoadSucceeded())
+
+ iconChangedSpy.wait()
+ compare(iconChangedSpy.count, 1)
+
+ var iconUrl = webEngineView.icon
+ compare(iconUrl, Qt.resolvedUrl(""))
+ }
+
+ function test_unavailableFavicon() {
+ compare(iconChangedSpy.count, 0)
+
+ var url = Qt.resolvedUrl("favicon-unavailable.html")
+ webEngineView.url = url
+ verify(webEngineView.waitForLoadSucceeded())
+
+ iconChangedSpy.wait()
+ compare(iconChangedSpy.count, 1)
+
+ var iconUrl = webEngineView.icon
+ compare(iconUrl, Qt.resolvedUrl("icons/unavailable.ico"))
+ }
+
+ function test_errorPageEnabled() {
+ skip("Error page does not work properly: QTBUG-48995")
+ WebEngine.settings.errorPageEnabled = true
+
+ compare(iconChangedSpy.count, 0)
+
+ var url = Qt.resolvedUrl("http://non.existent/url")
+ webEngineView.url = url
+ verify(webEngineView.testSupport.waitForErrorPageLoadSucceeded())
+
+ iconChangedSpy.wait()
+ // Icon is reseted at load start.
+ // Load is started twice: once for unavailale page then error page
+ compare(iconChangedSpy.count, 2)
+
+ var iconUrl = webEngineView.icon
+ compare(iconUrl, Qt.resolvedUrl(""))
+ }
+
+ function test_errorPageDisabled() {
+ WebEngine.settings.errorPageEnabled = false
+
+ compare(iconChangedSpy.count, 0)
+
+ var url = Qt.resolvedUrl("http://non.existent/url")
+ webEngineView.url = url
+ verify(webEngineView.waitForLoadFailed())
+
+ iconChangedSpy.wait()
+ compare(iconChangedSpy.count, 1)
+
+ var iconUrl = webEngineView.icon
+ compare(iconUrl, Qt.resolvedUrl(""))
+ }
+
+ function test_touchIcon() {
+ compare(iconChangedSpy.count, 0)
+
+ var url = Qt.resolvedUrl("favicon-touch.html")
+ webEngineView.url = url
+ verify(webEngineView.waitForLoadSucceeded())
+
+ iconChangedSpy.wait()
+ compare(iconChangedSpy.count, 1)
+
+ var iconUrl = webEngineView.icon
+ compare(iconUrl, Qt.resolvedUrl(""))
+ }
+ }
+}
diff --git a/tests/auto/quick/qmltests/data/tst_favIconLoad.qml b/tests/auto/quick/qmltests/data/tst_faviconImage.qml
index 2527cc740..603f76954 100644
--- a/tests/auto/quick/qmltests/data/tst_favIconLoad.qml
+++ b/tests/auto/quick/qmltests/data/tst_faviconImage.qml
@@ -28,7 +28,7 @@
import QtQuick 2.0
import QtTest 1.0
-import QtWebEngine 1.2
+import QtWebEngine 1.3
TestWebEngineView {
id: webEngineView
@@ -36,25 +36,19 @@ TestWebEngineView {
height: 400
SignalSpy {
- id: spy
+ id: iconChangedSpy
target: webEngineView
signalName: "iconChanged"
}
- // FIXME: This test is flaky if the loading of the icon image is asynchronous,
- // because the iconChanged signal is emitted before the image has been downloaded.
- // We can set this property to true after we have some kind of favicon downloading
- // logic in the WebEngine.
-
Image {
- id: favicon
- asynchronous: false
+ id: faviconImage
source: webEngineView.icon
}
TestCase {
id: test
- name: "WebEngineViewLoadFavIcon"
+ name: "WebEngineFaviconImage"
when: windowShown
function init() {
@@ -62,31 +56,70 @@ TestWebEngineView {
// If this is not the first test, then load a blank page without favicon, restoring the initial state.
webEngineView.url = 'about:blank'
verify(webEngineView.waitForLoadSucceeded())
- spy.wait()
+ iconChangedSpy.wait()
}
- spy.clear()
+
+ iconChangedSpy.clear()
}
- function test_favIconLoad() {
- compare(spy.count, 0)
+ function test_faviconImageLoad() {
+ compare(iconChangedSpy.count, 0)
+
var url = Qt.resolvedUrl("favicon.html")
webEngineView.url = url
verify(webEngineView.waitForLoadSucceeded())
- spy.wait()
- compare(spy.count, 1)
- compare(favicon.width, 48)
- compare(favicon.height, 48)
+
+ iconChangedSpy.wait()
+ compare(iconChangedSpy.count, 1)
+
+ compare(faviconImage.width, 48)
+ compare(faviconImage.height, 48)
}
- function test_favIconLoadEncodedUrl() {
- compare(spy.count, 0)
+ function test_faviconImageLoadEncodedUrl() {
+ compare(iconChangedSpy.count, 0)
+
var url = Qt.resolvedUrl("favicon2.html?favicon=load should work with#whitespace!")
webEngineView.url = url
verify(webEngineView.waitForLoadSucceeded())
- spy.wait()
- compare(spy.count, 1)
- compare(favicon.width, 16)
- compare(favicon.height, 16)
+
+ iconChangedSpy.wait()
+ compare(iconChangedSpy.count, 1)
+
+ compare(faviconImage.width, 16)
+ compare(faviconImage.height, 16)
+ }
+
+ function test_bestFaviconImage() {
+ compare(iconChangedSpy.count, 0)
+ var url, iconUrl
+
+ url = Qt.resolvedUrl("favicon-misc.html")
+ webEngineView.url = url
+ verify(webEngineView.waitForLoadSucceeded())
+
+ iconChangedSpy.wait()
+ compare(iconChangedSpy.count, 1)
+
+ iconUrl = webEngineView.icon
+ // Touch icon is ignored
+ compare(iconUrl, Qt.resolvedUrl("icons/qt32.ico"))
+ compare(faviconImage.width, 32)
+ compare(faviconImage.height, 32)
+
+ iconChangedSpy.clear()
+
+ url = Qt.resolvedUrl("favicon-shortcut.html")
+ webEngineView.url = url
+ verify(webEngineView.waitForLoadSucceeded())
+
+ iconChangedSpy.wait()
+ compare(iconChangedSpy.count, 1)
+
+ iconUrl = webEngineView.icon
+ compare(iconUrl, Qt.resolvedUrl("icons/qt144.png"))
+ compare(faviconImage.width, 144)
+ compare(faviconImage.height, 144)
}
}
}
diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro
index 57649384d..d1849d020 100644
--- a/tests/auto/quick/qmltests/qmltests.pro
+++ b/tests/auto/quick/qmltests/qmltests.pro
@@ -15,8 +15,12 @@ OTHER_FILES += \
$$PWD/data/confirmclose.html \
$$PWD/data/directoryupload.html \
$$PWD/data/favicon.html \
- $$PWD/data/favicon.png \
$$PWD/data/favicon2.html \
+ $$PWD/data/favicon-misc.html \
+ $$PWD/data/favicon-single.html \
+ $$PWD/data/favicon-shortcut.html \
+ $$PWD/data/favicon-touch.html \
+ $$PWD/data/favicon-unavailable.html \
$$PWD/data/forms.html \
$$PWD/data/geolocation.html \
$$PWD/data/javascript.html \
@@ -34,7 +38,8 @@ OTHER_FILES += \
$$PWD/data/titleupdate.js \
$$PWD/data/tst_desktopBehaviorLoadHtml.qml \
$$PWD/data/tst_download.qml \
- $$PWD/data/tst_favIconLoad.qml \
+ $$PWD/data/tst_favicon.qml \
+ $$PWD/data/tst_faviconImage.qml \
$$PWD/data/tst_filePicker.qml \
$$PWD/data/tst_formValidation.qml \
$$PWD/data/tst_geopermission.qml \
@@ -55,6 +60,10 @@ OTHER_FILES += \
$$PWD/data/tst_userScripts.qml \
$$PWD/data/tst_webchannel.qml \
$$PWD/data/tst_keyboardModifierMapping.qml \
+ $$PWD/data/icons/favicon.png \
+ $$PWD/data/icons/small-favicon.png \
+ $$PWD/data/icons/qt144.png \
+ $$PWD/data/icons/qt32.ico \
$$PWD/mock-delegates/QtWebEngine/UIDelegates/AlertDialog.qml \
$$PWD/mock-delegates/QtWebEngine/UIDelegates/ConfirmDialog.qml \
$$PWD/mock-delegates/QtWebEngine/UIDelegates/FilePicker.qml \
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/qwebenginefaviconmanager.pro b/tests/auto/widgets/qwebenginefaviconmanager/qwebenginefaviconmanager.pro
new file mode 100644
index 000000000..70786e70f
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/qwebenginefaviconmanager.pro
@@ -0,0 +1,2 @@
+include(../tests.pri)
+QT *= core-private gui-private
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-misc.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-misc.html
new file mode 100644
index 000000000..9e788bdf4
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-misc.html
@@ -0,0 +1,11 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="shortcut icon" href="icons/qt32.ico" />
+ <link rel="apple-touch-icon" href="icons/qt144.png" />
+ <link rel="shortcut icon" href="icons/unavailable.ico" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-shortcut.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-shortcut.html
new file mode 100644
index 000000000..786cdb816
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-shortcut.html
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="shortcut icon" href="icons/qt32.ico" />
+ <link rel="shortcut icon" href="icons/qt144.png" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-single.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-single.html
new file mode 100644
index 000000000..eb4675c75
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-single.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="shortcut icon" href="icons/qt32.ico" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-touch.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-touch.html
new file mode 100644
index 000000000..271783434
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-touch.html
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="apple-touch-icon" href="icons/qt32.ico" />
+ <link rel="apple-touch-icon" href="icons/qt144.png" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-unavailable.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-unavailable.html
new file mode 100644
index 000000000..c45664294
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-unavailable.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Favicon Test</title>
+ <link rel="shortcut icon" href="icons/unavailable.ico" />
+ </head>
+ <body>
+ <h1>Favicon Test</h1>
+ </body>
+</html>
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt144.png b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt144.png
new file mode 100644
index 000000000..050b1e066
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt144.png
Binary files differ
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt32.ico b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt32.ico
new file mode 100644
index 000000000..2f6fcb5bc
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt32.ico
Binary files differ
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/test1.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/test1.html
new file mode 100644
index 000000000..b323f966e
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/test1.html
@@ -0,0 +1 @@
+<html><body><p>Some text 1</p></body></html>
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.cpp b/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.cpp
new file mode 100644
index 000000000..ececb0efd
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.cpp
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include "../util.h"
+
+#include <qwebenginepage.h>
+#include <qwebenginesettings.h>
+#include <qwebengineview.h>
+
+
+class tst_QWebEngineFaviconManager : public QObject {
+ Q_OBJECT
+
+public Q_SLOTS:
+ void init();
+ void initTestCase();
+ void cleanupTestCase();
+ void cleanup();
+
+private Q_SLOTS:
+ void faviconLoad();
+ void faviconLoadFromResources();
+ void faviconLoadEncodedUrl();
+ void noFavicon();
+ void aboutBlank();
+ void unavailableFavicon();
+ void errorPageEnabled();
+ void errorPageDisabled();
+ void bestFavicon();
+ void touchIcon();
+
+private:
+ QWebEngineView* m_view;
+ QWebEnginePage* m_page;
+};
+
+
+void tst_QWebEngineFaviconManager::init()
+{
+ m_view = new QWebEngineView();
+ m_page = m_view->page();
+}
+
+
+void tst_QWebEngineFaviconManager::initTestCase()
+{
+}
+
+void tst_QWebEngineFaviconManager::cleanupTestCase()
+{
+}
+
+
+void tst_QWebEngineFaviconManager::cleanup()
+{
+ delete m_view;
+}
+
+void tst_QWebEngineFaviconManager::faviconLoad()
+{
+ if (!QDir(TESTS_SOURCE_DIR).exists())
+ W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
+
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+
+ QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-single.html"));
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qt32.ico")));
+}
+
+void tst_QWebEngineFaviconManager::faviconLoadFromResources()
+{
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+
+ QUrl url = QUrl("qrc:/resources/favicon-single.html");
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QCOMPARE(iconUrl, QUrl("qrc:/resources/icons/qt32.ico"));
+}
+
+void tst_QWebEngineFaviconManager::faviconLoadEncodedUrl()
+{
+ if (!QDir(TESTS_SOURCE_DIR).exists())
+ W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
+
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+
+ QString urlString = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-single.html")).toString();
+ QUrl url = QUrl(urlString + QLatin1String("?favicon=load should work with#whitespace!"));
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qt32.ico")));
+}
+
+void tst_QWebEngineFaviconManager::noFavicon()
+{
+ if (!QDir(TESTS_SOURCE_DIR).exists())
+ W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
+
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+
+ QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/test1.html"));
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QVERIFY(iconUrl.isEmpty());
+}
+
+void tst_QWebEngineFaviconManager::aboutBlank()
+{
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+
+ QUrl url = QUrl("about:blank");
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QVERIFY(iconUrl.isEmpty());
+}
+
+void tst_QWebEngineFaviconManager::unavailableFavicon()
+{
+ if (!QDir(TESTS_SOURCE_DIR).exists())
+ W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
+
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+
+ QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-unavailable.html"));
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/unavailable.ico")));
+}
+
+void tst_QWebEngineFaviconManager::errorPageEnabled()
+{
+ m_page->settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, true);
+
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+
+ QUrl url = QUrl(QUrl("http://non.existent/url"));
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ // Icon is reseted at load start.
+ // Load is started twice: once for unavailale page then error page
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 2);
+
+ QUrl iconUrl;
+ iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QVERIFY(iconUrl.isEmpty());
+ iconUrl = iconUrlChangedSpy.at(1).at(0).toString();
+ QVERIFY(iconUrl.isEmpty());
+}
+
+void tst_QWebEngineFaviconManager::errorPageDisabled()
+{
+ m_page->settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false);
+
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+
+ QUrl url = QUrl(QUrl("http://non.existent/url"));
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QVERIFY(iconUrl.isEmpty());
+}
+
+void tst_QWebEngineFaviconManager::bestFavicon()
+{
+ if (!QDir(TESTS_SOURCE_DIR).exists())
+ W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
+
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+ QUrl url, iconUrl;
+
+ url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-misc.html"));
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ // Touch icon is ignored
+ QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qt32.ico")));
+
+ loadFinishedSpy.clear();
+ iconUrlChangedSpy.clear();
+
+ url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-shortcut.html"));
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qt144.png")));
+}
+
+void tst_QWebEngineFaviconManager::touchIcon()
+{
+ if (!QDir(TESTS_SOURCE_DIR).exists())
+ W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
+
+ QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
+ QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl)));
+
+ QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-touch.html"));
+ m_page->load(url);
+
+ QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE(iconUrlChangedSpy.count(), 1);
+
+ QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString();
+ QVERIFY(iconUrl.isEmpty());
+}
+
+QTEST_MAIN(tst_QWebEngineFaviconManager)
+
+#include "tst_qwebenginefaviconmanager.moc"
diff --git a/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.qrc b/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.qrc
new file mode 100644
index 000000000..65d8ed928
--- /dev/null
+++ b/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.qrc
@@ -0,0 +1,12 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>resources/favicon-misc.html</file>
+ <file>resources/favicon-shortcut.html</file>
+ <file>resources/favicon-single.html</file>
+ <file>resources/favicon-touch.html</file>
+ <file>resources/favicon-unavailable.html</file>
+ <file>resources/icons/qt144.png</file>
+ <file>resources/icons/qt32.ico</file>
+ <file>resources/test1.html</file>
+</qresource>
+</RCC>
diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro
index 986d5bbee..c65d7dd6c 100644
--- a/tests/auto/widgets/widgets.pro
+++ b/tests/auto/widgets/widgets.pro
@@ -2,6 +2,7 @@ TEMPLATE = subdirs
SUBDIRS += \
qwebengineaccessibility \
+ qwebenginefaviconmanager \
qwebenginepage \
qwebenginehistory \
qwebenginehistoryinterface \