/**************************************************************************** ** ** 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/favicon_status.h" #include "content/public/browser/navigation_entry.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 #include 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) { static int fakeId = 0; int id; bool cached = 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())); } Q_ASSERT(!m_inProgressRequests.contains(id)); m_inProgressRequests.insert(id, url); return id; } void FaviconManagerPrivate::iconDownloadFinished(int id, int status, const GURL &url, const std::vector &bitmaps, const std::vector &original_bitmap_sizes) { Q_UNUSED(status); Q_UNUSED(url); Q_UNUSED(original_bitmap_sizes); storeIcon(id, toQIcon(bitmaps)); } /* Pending requests are used to mark icons that are already downloaded (cached icons or icons * stored in qrc). These requests are also stored in the m_inProgressRequests but the corresponding * icons are stored in m_icons explicitly by this function. It is necessary to avoid * m_inProgressRequests being emptied right before the next icon is added by a downloadIcon() call. */ void FaviconManagerPrivate::downloadPendingRequests() { for (auto it = m_pendingRequests.cbegin(), end = m_pendingRequests.cend(); it != end; ++it) { QIcon icon; QUrl requestUrl = it.value(); if (isResourceUrl(requestUrl) && !m_icons.contains(requestUrl)) icon = QIcon(requestUrl.toString().remove(0, 3)); storeIcon(it.key(), icon); } m_pendingRequests.clear(); } void FaviconManagerPrivate::storeIcon(int id, const QIcon &icon) { Q_Q(FaviconManager); Q_ASSERT(m_inProgressRequests.contains(id)); QUrl requestUrl = m_inProgressRequests[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); } } } } else if (id >= 0) { // Reset size if icon cannot be downloaded faviconInfo.size = QSize(0, 0); } m_inProgressRequests.remove(id); if (m_inProgressRequests.isEmpty()) propagateIcon(); } void FaviconManagerPrivate::propagateIcon() const { Q_Q(const FaviconManager); QUrl iconUrl; const QList &faviconInfoList = q->getFaviconInfoList(true /* candidates only */); unsigned bestArea = 0; for (auto it = faviconInfoList.cbegin(), end = faviconInfoList.cend(); it != end; ++it) { if (it->type != FaviconInfo::Favicon) continue; if (it->isValid() && bestArea < area(it->size)) { iconUrl = it->url; bestArea = area(it->size); } } content::NavigationEntry *entry = m_webContents->GetController().GetVisibleEntry(); if (entry) { content::FaviconStatus &favicon = entry->GetFavicon(); favicon.url = toGurl(iconUrl); favicon.valid = true; } m_viewClient->iconChanged(iconUrl); } FaviconManager::FaviconManager(FaviconManagerPrivate *d) : m_hasCandidate(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 FaviconManager::getFaviconInfoList(bool candidatesOnly) const { QList faviconInfoList = m_faviconInfoMap.values(); if (candidatesOnly) { QMutableListIterator it(faviconInfoList); while (it.hasNext()) { if (!it.next().candidate) it.remove(); } } return faviconInfoList; } void FaviconManager::update(const QList &candidates) { Q_D(FaviconManager); updateCandidates(candidates); const QList &faviconInfoList = getFaviconInfoList(true /* candidates only */); for (auto it = faviconInfoList.cbegin(), end = faviconInfoList.cend(); it != end; ++it) { if (it->type != FaviconInfo::Favicon) continue; if (it->isValid()) d->downloadIcon(it->url); } d->downloadPendingRequests(); // Reset icon if nothing was downloaded if (d->m_inProgressRequests.isEmpty()) { content::NavigationEntry *entry = d->m_webContents->GetController().GetVisibleEntry(); if (entry && !entry->GetFavicon().valid) d->m_viewClient->iconChanged(QUrl()); } } void FaviconManager::updateCandidates(const QList &candidates) { m_hasCandidate = candidates.count(); for (FaviconInfo candidateFaviconInfo : candidates) { const QUrl &candidateUrl = candidateFaviconInfo.url; if (!m_faviconInfoMap.contains(candidateUrl)) m_faviconInfoMap.insert(candidateUrl, candidateFaviconInfo); else { // The same icon can be used for more than one page with different types. m_faviconInfoMap[candidateUrl].type = candidateFaviconInfo.type; } m_faviconInfoMap[candidateUrl].candidate = true; } } void FaviconManager::resetCandidates() { m_hasCandidate = false; for (auto it = m_faviconInfoMap.begin(), end = m_faviconInfoMap.end(); it != end; ++it) it->candidate = false; } bool FaviconManager::hasCandidate() const { return m_hasCandidate; } 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