File indexing completed on 2024-05-12 04:58:27
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "iconprovider.h" 0019 #include "mainapplication.h" 0020 #include "networkmanager.h" 0021 #include "sqldatabase.h" 0022 #include "autosaver.h" 0023 #include "webview.h" 0024 #include "qztools.h" 0025 0026 #include <QTimer> 0027 #include <QBuffer> 0028 #include <QFutureWatcher> 0029 #include <QtConcurrent/QtConcurrentRun> 0030 0031 Q_GLOBAL_STATIC(IconProvider, qz_icon_provider) 0032 0033 static QByteArray encodeUrl(const QUrl &url) 0034 { 0035 return url.toEncoded(QUrl::RemoveFragment | QUrl::StripTrailingSlash); 0036 } 0037 0038 IconProvider::IconProvider() 0039 : QWidget() 0040 { 0041 m_autoSaver = new AutoSaver(this); 0042 connect(m_autoSaver, &AutoSaver::save, this, &IconProvider::saveIconsToDatabase); 0043 } 0044 0045 void IconProvider::saveIcon(WebView* view) 0046 { 0047 // Don't save icons in private mode. 0048 if (mApp->isPrivate()) { 0049 return; 0050 } 0051 0052 const QIcon icon = view->icon(true); 0053 if (icon.isNull()) { 0054 return; 0055 } 0056 0057 const QStringList ignoredSchemes = { 0058 QStringLiteral("falkon"), 0059 QStringLiteral("ftp"), 0060 QStringLiteral("file"), 0061 QStringLiteral("view-source"), 0062 QStringLiteral("data"), 0063 QStringLiteral("about") 0064 }; 0065 0066 if (ignoredSchemes.contains(view->url().scheme())) { 0067 return; 0068 } 0069 0070 for (int i = 0; i < m_iconBuffer.size(); ++i) { 0071 if (m_iconBuffer[i].first == view->url()) { 0072 m_iconBuffer.removeAt(i); 0073 break; 0074 } 0075 } 0076 0077 BufferedIcon item; 0078 item.first = view->url(); 0079 item.second = icon.pixmap(16).toImage(); 0080 0081 m_autoSaver->changeOccurred(); 0082 m_iconBuffer.append(item); 0083 } 0084 0085 QIcon IconProvider::bookmarkIcon() const 0086 { 0087 return QIcon::fromTheme(QSL("bookmarks"), m_bookmarkIcon); 0088 } 0089 0090 void IconProvider::setBookmarkIcon(const QIcon &icon) 0091 { 0092 m_bookmarkIcon = icon; 0093 } 0094 0095 QIcon IconProvider::standardIcon(QStyle::StandardPixmap icon) 0096 { 0097 switch (icon) { 0098 case QStyle::SP_MessageBoxCritical: 0099 return QIcon::fromTheme(QSL("dialog-error"), QApplication::style()->standardIcon(icon)); 0100 0101 case QStyle::SP_MessageBoxInformation: 0102 return QIcon::fromTheme(QSL("dialog-information"), QApplication::style()->standardIcon(icon)); 0103 0104 case QStyle::SP_MessageBoxQuestion: 0105 return QIcon::fromTheme(QSL("dialog-question"), QApplication::style()->standardIcon(icon)); 0106 0107 case QStyle::SP_MessageBoxWarning: 0108 return QIcon::fromTheme(QSL("dialog-warning"), QApplication::style()->standardIcon(icon)); 0109 0110 case QStyle::SP_DialogCloseButton: 0111 return QIcon::fromTheme(QSL("dialog-close"), QApplication::style()->standardIcon(icon)); 0112 0113 case QStyle::SP_BrowserStop: 0114 return QIcon::fromTheme(QSL("process-stop"), QApplication::style()->standardIcon(icon)); 0115 0116 case QStyle::SP_BrowserReload: 0117 return QIcon::fromTheme(QSL("view-refresh"), QApplication::style()->standardIcon(icon)); 0118 0119 case QStyle::SP_FileDialogToParent: 0120 return QIcon::fromTheme(QSL("go-up"), QApplication::style()->standardIcon(icon)); 0121 0122 case QStyle::SP_ArrowUp: 0123 return QIcon::fromTheme(QSL("go-up"), QApplication::style()->standardIcon(icon)); 0124 0125 case QStyle::SP_ArrowDown: 0126 return QIcon::fromTheme(QSL("go-down"), QApplication::style()->standardIcon(icon)); 0127 0128 case QStyle::SP_ArrowForward: 0129 if (QApplication::layoutDirection() == Qt::RightToLeft) { 0130 return QIcon::fromTheme(QSL("go-previous"), QApplication::style()->standardIcon(icon)); 0131 } 0132 return QIcon::fromTheme(QSL("go-next"), QApplication::style()->standardIcon(icon)); 0133 0134 case QStyle::SP_ArrowBack: 0135 if (QApplication::layoutDirection() == Qt::RightToLeft) { 0136 return QIcon::fromTheme(QSL("go-next"), QApplication::style()->standardIcon(icon)); 0137 } 0138 return QIcon::fromTheme(QSL("go-previous"), QApplication::style()->standardIcon(icon)); 0139 0140 default: 0141 return QApplication::style()->standardIcon(icon); 0142 } 0143 } 0144 0145 QIcon IconProvider::newTabIcon() 0146 { 0147 return QIcon::fromTheme(QSL("tab-new"), QIcon(QSL(":/icons/menu/tab-new.svg"))); 0148 } 0149 0150 QIcon IconProvider::newWindowIcon() 0151 { 0152 return QIcon::fromTheme(QSL("window-new"), QIcon(QSL(":/icons/menu/window-new.svg"))); 0153 } 0154 0155 QIcon IconProvider::privateBrowsingIcon() 0156 { 0157 return QIcon::fromTheme(QSL("view-private-symbolic"), QIcon(QSL(":/icons/menu/privatebrowsing.png"))); 0158 } 0159 0160 QIcon IconProvider::settingsIcon() 0161 { 0162 return QIcon::fromTheme(QSL("configure"), QIcon(QSL(":/icons/menu/settings.svg"))); 0163 } 0164 0165 QIcon IconProvider::emptyWebIcon() 0166 { 0167 return QPixmap::fromImage(instance()->emptyWebImage()); 0168 } 0169 0170 QImage IconProvider::emptyWebImage() 0171 { 0172 if (instance()->m_emptyWebImage.isNull()) { 0173 instance()->m_emptyWebImage = QIcon(QSL(":icons/other/webpage.svg")).pixmap(16).toImage(); 0174 } 0175 0176 return instance()->m_emptyWebImage; 0177 } 0178 0179 QIcon IconProvider::iconForUrl(const QUrl &url, bool allowNull) 0180 { 0181 return instance()->iconFromImage(imageForUrl(url, allowNull)); 0182 } 0183 0184 QImage IconProvider::imageForUrl(const QUrl &url, bool allowNull) 0185 { 0186 if (url.path().isEmpty()) { 0187 return allowNull ? QImage() : IconProvider::emptyWebImage(); 0188 } 0189 0190 QMutexLocker locker(&instance()->m_iconCacheMutex); 0191 0192 const QByteArray encodedUrl = encodeUrl(url); 0193 0194 if (QImage *img = instance()->m_urlImageCache.object(encodedUrl)) { 0195 return img->isNull() && !allowNull ? IconProvider::emptyWebImage() : *img; 0196 } 0197 0198 const auto iconBuffer = instance()->m_iconBuffer; 0199 for (const BufferedIcon &ic : iconBuffer) { 0200 if (encodeUrl(ic.first) == encodedUrl) { 0201 return ic.second; 0202 } 0203 } 0204 0205 QSqlQuery query(SqlDatabase::instance()->database()); 0206 query.prepare(QSL("SELECT icon FROM icons WHERE url GLOB ? LIMIT 1")); 0207 query.addBindValue(QSL("%1*").arg(QzTools::escapeSqlGlobString(QString::fromUtf8(encodedUrl)))); 0208 query.exec(); 0209 0210 auto *img = new QImage; 0211 if (query.next()) { 0212 img->loadFromData(query.value(0).toByteArray()); 0213 } 0214 instance()->m_urlImageCache.insert(encodedUrl, img); 0215 0216 return img->isNull() && !allowNull ? IconProvider::emptyWebImage() : *img; 0217 } 0218 0219 QIcon IconProvider::iconForDomain(const QUrl &url, bool allowNull) 0220 { 0221 return instance()->iconFromImage(imageForDomain(url, allowNull)); 0222 } 0223 0224 QImage IconProvider::imageForDomain(const QUrl &url, bool allowNull) 0225 { 0226 if (url.host().isEmpty()) { 0227 return allowNull ? QImage() : IconProvider::emptyWebImage(); 0228 } 0229 0230 QMutexLocker locker(&instance()->m_iconCacheMutex); 0231 0232 const auto iconBuffer = instance()->m_iconBuffer; 0233 for (const BufferedIcon &ic : iconBuffer) { 0234 if (ic.first.host() == url.host()) { 0235 return ic.second; 0236 } 0237 } 0238 0239 QSqlQuery query(SqlDatabase::instance()->database()); 0240 query.prepare(QSL("SELECT icon FROM icons WHERE url GLOB ? LIMIT 1")); 0241 query.addBindValue(QSL("*%1*").arg(QzTools::escapeSqlGlobString(url.host()))); 0242 query.exec(); 0243 0244 if (query.next()) { 0245 return QImage::fromData(query.value(0).toByteArray()); 0246 } 0247 0248 return allowNull ? QImage() : IconProvider::emptyWebImage(); 0249 } 0250 0251 IconProvider* IconProvider::instance() 0252 { 0253 return qz_icon_provider(); 0254 } 0255 0256 void IconProvider::saveIconsToDatabase() 0257 { 0258 QMutexLocker locker(&instance()->m_iconCacheMutex); 0259 0260 for (const BufferedIcon &ic : std::as_const(m_iconBuffer)) { 0261 QByteArray ba; 0262 QBuffer buffer(&ba); 0263 buffer.open(QIODevice::WriteOnly); 0264 ic.second.save(&buffer, "PNG"); 0265 0266 const QByteArray encodedUrl = encodeUrl(ic.first); 0267 m_urlImageCache.remove(encodedUrl); 0268 0269 auto job = new SqlQueryJob(QSL("INSERT OR REPLACE INTO icons (icon, url) VALUES (?,?)"), this); 0270 job->addBindValue(buffer.data()); 0271 job->addBindValue(QString::fromUtf8(encodedUrl)); 0272 job->start(); 0273 } 0274 0275 m_iconBuffer.clear(); 0276 } 0277 0278 void IconProvider::clearOldIconsInDatabase() 0279 { 0280 // Delete icons for entries older than 6 months 0281 const QDateTime date = QDateTime::currentDateTime().addMonths(-6); 0282 0283 QSqlQuery query(SqlDatabase::instance()->database()); 0284 query.prepare(QSL("DELETE FROM icons WHERE url IN (SELECT url FROM history WHERE date < ?)")); 0285 query.addBindValue(date.toMSecsSinceEpoch()); 0286 query.exec(); 0287 0288 query.clear(); 0289 query.exec(QSL("VACUUM")); 0290 } 0291 0292 QIcon IconProvider::iconFromImage(const QImage &image) 0293 { 0294 return QIcon(QPixmap::fromImage(image)); 0295 }