Falkon Develop
Cross-platform Qt-based web browser
siteinfo.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
18#include "siteinfo.h"
19#include "ui_siteinfo.h"
20#include "listitemdelegate.h"
21#include "webview.h"
22#include "webpage.h"
23#include "mainapplication.h"
24#include "downloaditem.h"
26#include "qztools.h"
27#include "iconprovider.h"
28#include "scripts.h"
29#include "networkmanager.h"
30#include "locationbar.h"
31
32#include <QMenu>
33#include <QMessageBox>
34#include <QFileDialog>
35#include <QNetworkDiskCache>
36#include <QClipboard>
37#include <QTimer>
38#include <QGraphicsPixmapItem>
39#include <QShortcut>
40#include <QListWidgetItem>
41
43 : QDialog(view)
44 , ui(new Ui::SiteInfo)
45 , m_certWidget(nullptr)
46 , m_view(view)
47 , m_imageReply(nullptr)
48 , m_baseUrl(view->url())
49{
50 setAttribute(Qt::WA_DeleteOnClose);
51 ui->setupUi(this);
52 ui->treeTags->setLayoutDirection(Qt::LeftToRight);
54
55 auto* delegate = new ListItemDelegate(24, ui->listWidget);
56 delegate->setUpdateParentHeight(true);
57 delegate->setUniformItemSizes(true);
58 ui->listWidget->setItemDelegate(delegate);
59
60 ui->listWidget->item(0)->setIcon(QIcon::fromTheme(QSL("document-properties"), QIcon(QSL(":/icons/preferences/document-properties.png"))));
61 ui->listWidget->item(1)->setIcon(QIcon::fromTheme(QSL("applications-graphics"), QIcon(QSL(":/icons/preferences/applications-graphics.png"))));
62 ui->listWidget->item(2)->setIcon(QIcon(QStringLiteral(":/icons/preferences/privacy.svg")));
63 ui->listWidget->item(0)->setSelected(true);
64
65 // General
66 ui->heading->setText(QSL("<b>%1</b>:").arg(m_view->title()));
67 ui->siteAddress->setText(m_view->url().toString());
68
69 if (m_view->url().scheme() == QL1S("https"))
70 ui->securityLabel->setText(tr("<b>Connection is Encrypted.</b>"));
71 else
72 ui->securityLabel->setText(tr("<b>Connection Not Encrypted.</b>"));
73
74 m_view->page()->runJavaScript(QSL("document.charset"), WebPage::SafeJsWorld, [this](const QVariant &res) {
75 ui->encodingLabel->setText(res.toString());
76 });
77
78 // Meta
79 m_view->page()->runJavaScript(Scripts::getAllMetaAttributes(), WebPage::SafeJsWorld, [this](const QVariant &res) {
80 const QVariantList &list = res.toList();
81 for (const QVariant &val : list) {
82 const QVariantMap &meta = val.toMap();
83 QString content = meta.value(QSL("content")).toString();
84 QString name = meta.value(QSL("name")).toString();
85
86 if (name.isEmpty())
87 name = meta.value(QSL("httpequiv")).toString();
88
89 if (content.isEmpty() || name.isEmpty())
90 continue;
91
92 auto* item = new QTreeWidgetItem(ui->treeTags);
93 item->setText(0, name);
94 item->setText(1, content);
95 ui->treeTags->addTopLevelItem(item);
96 }
97 });
98
99 // Images
100 m_view->page()->runJavaScript(Scripts::getAllImages(), WebPage::SafeJsWorld, [this](const QVariant &res) {
101 const QVariantList &list = res.toList();
102 for (const QVariant &val : list) {
103 const QVariantMap &img = val.toMap();
104 QString src = img.value(QSL("src")).toString();
105 QString alt = img.value(QSL("alt")).toString();
106 if (alt.isEmpty()) {
107 if (src.indexOf(QLatin1Char('/')) == -1) {
108 alt = src;
109 }
110 else {
111 int pos = src.lastIndexOf(QLatin1Char('/'));
112 alt = src.mid(pos);
113 alt.remove(QLatin1Char('/'));
114 }
115 }
116
117 if (src.isEmpty() || alt.isEmpty())
118 continue;
119
120 auto* item = new QTreeWidgetItem(ui->treeImages);
121 item->setText(0, alt);
122 item->setText(1, src);
123 ui->treeImages->addTopLevelItem(item);
124 }
125 });
126
127 /* Permissions */
128 addSiteSettings();
129
130 connect(ui->saveButton, SIGNAL(clicked(QAbstractButton*)), this, SLOT(saveImage()));
131 connect(ui->listWidget, SIGNAL(currentRowChanged(int)), ui->stackedWidget, SLOT(setCurrentIndex(int)));
132 connect(ui->treeImages, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(showImagePreview(QTreeWidgetItem*)));
133 connect(ui->treeImages, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(imagesCustomContextMenuRequested(QPoint)));
134 connect(ui->treeTags, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(tagsCustomContextMenuRequested(QPoint)));
135 connect(this, &QDialog::accepted, this, &SiteInfo::saveSiteSettings);
136
137 auto *shortcutTagsCopyAll = new QShortcut(QKeySequence(QSL("Ctrl+C")), ui->treeTags);
138 shortcutTagsCopyAll->setContext(Qt::WidgetShortcut);
139 connect(shortcutTagsCopyAll, &QShortcut::activated, this, [=]{copySelectedItems(ui->treeTags, false);});
140
141 auto *shortcutTagsCopyValues = new QShortcut(QKeySequence(QSL("Ctrl+Shift+C")), ui->treeTags);
142 shortcutTagsCopyValues->setContext(Qt::WidgetShortcut);
143 connect(shortcutTagsCopyValues, &QShortcut::activated, this, [=]{copySelectedItems(ui->treeTags, true);});
144
145 auto *shortcutImagesCopyAll = new QShortcut(QKeySequence(QSL("Ctrl+C")), ui->treeImages);
146 shortcutImagesCopyAll->setContext(Qt::WidgetShortcut);
147 connect(shortcutImagesCopyAll, &QShortcut::activated, this, [=]{copySelectedItems(ui->treeImages, false);});
148
149 auto *shortcutImagesCopyValues = new QShortcut(QKeySequence(QSL("Ctrl+Shift+C")), ui->treeImages);
150 shortcutImagesCopyValues->setContext(Qt::WidgetShortcut);
151 connect(shortcutImagesCopyValues, &QShortcut::activated, this, [=]{copySelectedItems(ui->treeImages, true);});
152
153 ui->treeImages->setContextMenuPolicy(Qt::CustomContextMenu);
154 ui->treeImages->sortByColumn(-1, Qt::AscendingOrder);
155
156 ui->treeTags->setContextMenuPolicy(Qt::CustomContextMenu);
157 ui->treeTags->sortByColumn(-1, Qt::AscendingOrder);
158
159 QzTools::setWmClass(QSL("Site Info"), this);
160}
161
162bool SiteInfo::canShowSiteInfo(const QUrl &url)
163{
164 if (LocationBar::convertUrlToText(url).isEmpty())
165 return false;
166
167 if (url.scheme() == QL1S("falkon") || url.scheme() == QL1S("view-source") || url.scheme() == QL1S("extension"))
168 return false;
169
170 return true;
171}
172
173void SiteInfo::imagesCustomContextMenuRequested(const QPoint &p)
174{
175 QTreeWidgetItem* item = ui->treeImages->itemAt(p);
176 if (!item) {
177 return;
178 }
179
180 QMenu menu;
181 menu.addAction(QIcon::fromTheme(QSL("edit-copy")), tr("Copy Image Location"), QKeySequence(QSL("Ctrl+C")), this, [=]{copySelectedItems(ui->treeImages, false);});
182 menu.addAction(tr("Copy Image Name"), QKeySequence(QSL("Ctrl+Shift+C")), this, [=]{copySelectedItems(ui->treeImages, true);});
183 menu.addSeparator();
184 menu.addAction(QIcon::fromTheme(QSL("document-save")), tr("Save Image to Disk"), this, SLOT(saveImage()));
185 menu.exec(ui->treeImages->viewport()->mapToGlobal(p));
186}
187
188void SiteInfo::tagsCustomContextMenuRequested(const QPoint &p)
189{
190 QTreeWidgetItem* item = ui->treeTags->itemAt(p);
191 if (!item) {
192 return;
193 }
194
195 QMenu menu;
196 menu.addAction(tr("Copy Values"), QKeySequence(QSL("Ctrl+C")), this, [=]{copySelectedItems(ui->treeTags, false);});
197 menu.addAction(tr("Copy Tags and Values"), QKeySequence(QSL("Ctrl+Shift+C")), this, [=]{copySelectedItems(ui->treeTags, true);});
198 menu.exec(ui->treeTags->viewport()->mapToGlobal(p));
199}
200
201void SiteInfo::copySelectedItems(const QTreeWidget* treeWidget, const bool both)
202{
203 QList<QTreeWidgetItem*> itemList = treeWidget->selectedItems();
204 QString tmpText = QSL("");
205
206 for (int i = 0; i < itemList.size(); ++i) {
207 if (i != 0) {
208 tmpText.append(QSL("\n"));
209 }
210 if (both) {
211 tmpText.append((itemList[i])->text(0));
212 tmpText.append(QSL("\t"));
213 }
214 tmpText.append((itemList[i])->text(1));
215 }
216 qApp->clipboard()->setText(tmpText);
217}
218
219void SiteInfo::saveImage()
220{
221 QTreeWidgetItem* item = ui->treeImages->currentItem();
222 if (!item) {
223 return;
224 }
225
226 if (!ui->mediaPreview->scene() || ui->mediaPreview->scene()->items().isEmpty())
227 return;
228
229 QGraphicsItem *graphicsItem = ui->mediaPreview->scene()->items().at(0);
230 auto *pixmapItem = static_cast<QGraphicsPixmapItem*>(graphicsItem);
231 if (graphicsItem->type() != QGraphicsPixmapItem::Type || !pixmapItem)
232 return;
233
234 if (!pixmapItem || pixmapItem->pixmap().isNull()) {
235 QMessageBox::warning(this, tr("Error!"), tr("This preview is not available!"));
236 return;
237 }
238
239 QString imageFileName = QzTools::getFileNameFromUrl(QUrl(item->text(1)));
240 int index = imageFileName.lastIndexOf(QLatin1Char('.'));
241 if (index != -1) {
242 imageFileName.truncate(index);
243 imageFileName.append(QL1S(".png"));
244 }
245
246 QString filePath = QzTools::getSaveFileName(QSL("SiteInfo-DownloadImage"), this, tr("Save image..."),
247 QDir::homePath() + QDir::separator() + imageFileName,
248 QSL("*.png"));
249 if (filePath.isEmpty()) {
250 return;
251 }
252
253 if (!pixmapItem->pixmap().save(filePath, "PNG")) {
254 QMessageBox::critical(this, tr("Error!"), tr("Cannot write to file!"));
255 return;
256 }
257}
258
259void SiteInfo::showLoadingText()
260{
261 delete ui->mediaPreview->scene();
262 auto* scene = new QGraphicsScene(ui->mediaPreview);
263
264 scene->addText(tr("Loading..."));
265
266 ui->mediaPreview->setScene(scene);
267}
268
269void SiteInfo::showPixmap(QPixmap pixmap)
270{
271 pixmap.setDevicePixelRatio(devicePixelRatioF());
272
273 delete ui->mediaPreview->scene();
274 auto* scene = new QGraphicsScene(ui->mediaPreview);
275
276 if (pixmap.isNull())
277 scene->addText(tr("Preview not available"));
278 else
279 scene->addPixmap(pixmap);
280
281 ui->mediaPreview->setScene(scene);
282}
283
284void SiteInfo::showImagePreview(QTreeWidgetItem *item)
285{
286 if ((!item) || (item->treeWidget()->selectedItems().length() > 1)) {
287 return;
288 }
289 QUrl imageUrl = QUrl::fromEncoded(item->text(1).toUtf8());
290 if (imageUrl.isRelative()) {
291 imageUrl = m_baseUrl.resolved(imageUrl);
292 }
293
294 QPixmap pixmap;
295 bool loading = false;
296
297 if (imageUrl.scheme() == QLatin1String("data")) {
298 QByteArray encodedUrl = item->text(1).toUtf8();
299 QByteArray imageData = encodedUrl.mid(encodedUrl.indexOf(',') + 1);
300 pixmap = QzTools::pixmapFromByteArray(imageData);
301 }
302 else if (imageUrl.scheme() == QLatin1String("file")) {
303 pixmap = QPixmap(imageUrl.toLocalFile());
304 }
305 else if (imageUrl.scheme() == QLatin1String("qrc")) {
306 pixmap = QPixmap(imageUrl.toString().mid(3)); // Remove qrc from url
307 }
308 else {
309 delete m_imageReply;
310 m_imageReply = mApp->networkManager()->get(QNetworkRequest(imageUrl));
311 connect(m_imageReply, &QNetworkReply::finished, this, [this]() {
312 if (m_imageReply->error() != QNetworkReply::NoError)
313 return;
314
315 const QByteArray &data = m_imageReply->readAll();
316 showPixmap(QPixmap::fromImage(QImage::fromData(data)));
317 });
318
319 loading = true;
320 showLoadingText();
321 }
322
323 if (!loading)
324 showPixmap(pixmap);
325}
326
328{
329 delete ui;
330 delete m_certWidget;
331}
332
333SiteInfoPermissionItem* SiteInfo::addPermissionOption(SiteSettingsManager::Permission perm)
334{
335 auto* listItem = new QListWidgetItem(ui->listPermissions);
336 auto* optionItem = new SiteInfoPermissionItem(perm, this);
337
338 ui->listPermissions->setItemWidget(listItem, optionItem);
339 listItem->setSizeHint(optionItem->sizeHint());
340
341 return optionItem;
342}
343
344void SiteInfo::addSiteSettings()
345{
346 auto siteSettings = mApp->siteSettingsManager()->getSiteSettings(m_baseUrl);
347 const auto supportedAttribute = mApp->siteSettingsManager()->getSupportedAttribute();
348 for (const auto &attribute : supportedAttribute) {
349 SiteInfoPermissionItem *item = addPermissionOption(siteSettings.attributes[attribute]);
350 item->setAttribute(attribute);
351 }
352 const auto supportedFeatures = mApp->siteSettingsManager()->getSupportedFeatures();
353 for (const auto &feature : supportedFeatures) {
354 SiteInfoPermissionItem *item = addPermissionOption(siteSettings.features[feature]);
355 item->setFeature(feature);
356 }
357 SiteInfoPermissionItem *item = addPermissionOption(siteSettings.AllowCookies);
359}
360
361void SiteInfo::saveSiteSettings()
362{
363 SiteSettings siteSettings;
364 int index = 0;
365 auto supportedAttribute = mApp->siteSettingsManager()->getSupportedAttribute();
366 auto supportedFeatures = mApp->siteSettingsManager()->getSupportedFeatures();
367
368 for (int i = 0; i < supportedAttribute.size(); ++i, ++index) {
369 auto* item = static_cast<SiteInfoPermissionItem*>(ui->listPermissions->itemWidget(ui->listPermissions->item(index)));
370 siteSettings.attributes[supportedAttribute[i]] = item->permission();
371 }
372 for (int i = 0; i < supportedFeatures.size(); ++i, ++index) {
373 auto* item = static_cast<SiteInfoPermissionItem*>(ui->listPermissions->itemWidget(ui->listPermissions->item(index)));
374 siteSettings.features[supportedFeatures[i]] = item->permission();
375 }
376 auto* item = static_cast<SiteInfoPermissionItem*>(ui->listPermissions->itemWidget(ui->listPermissions->item(index++)));
377 siteSettings.AllowCookies = item->permission();
378 siteSettings.ZoomLevel = -1;
379
380 siteSettings.server = m_baseUrl.host();
381
382 auto storedSiteSettings = mApp->siteSettingsManager()->getSiteSettings(m_baseUrl);
383
384 if (!(siteSettings == storedSiteSettings)) {
385 mApp->siteSettingsManager()->setSiteSettings(siteSettings);
386
387#if QTWEBENGINECORE_VERSION >= QT_VERSION_CHECK(6, 7, 0)
388 if (siteSettings.attributes[QWebEngineSettings::ForceDarkMode] != storedSiteSettings.attributes[QWebEngineSettings::ForceDarkMode]) {
389 bool enableForceDarkMode = false;
390
391 if (siteSettings.attributes[QWebEngineSettings::ForceDarkMode] == SiteSettingsManager::Default) {
392 enableForceDarkMode = mApp->siteSettingsManager()->getDefaultPermission(QWebEngineSettings::ForceDarkMode) == SiteSettingsManager::Allow;
393 }
394 else {
395 enableForceDarkMode = siteSettings.attributes[QWebEngineSettings::ForceDarkMode] == SiteSettingsManager::Allow;
396 }
397 m_view->page()->settings()->setAttribute(
398 QWebEngineSettings::ForceDarkMode,
399 enableForceDarkMode
400 );
401 }
402#endif
403 }
404}
static QString convertUrlToText(const QUrl &url)
static void setWmClass(const QString &name, const QWidget *widget)
Definition: qztools.cpp:874
static QString getSaveFileName(const QString &name, QWidget *parent=nullptr, const QString &caption=QString(), const QString &dir=QString(), const QString &filter=QString(), QString *selectedFilter=nullptr, QFileDialog::Options options=QFileDialog::Options())
Definition: qztools.cpp:709
static void centerWidgetOnScreen(QWidget *w)
Definition: qztools.cpp:116
static QPixmap pixmapFromByteArray(const QByteArray &data)
Definition: qztools.cpp:74
static QString getFileNameFromUrl(const QUrl &url)
Definition: qztools.cpp:286
static QString getAllMetaAttributes()
Definition: scripts.cpp:319
static QString getAllImages()
Definition: scripts.cpp:301
static bool canShowSiteInfo(const QUrl &url)
Definition: siteinfo.cpp:162
SiteInfo(WebView *view)
Definition: siteinfo.cpp:42
SiteSettingsManager::Permission permission() const
void setOption(const SiteSettingsManager::PageOptions &option)
void setFeature(const QWebEnginePage::Feature &feature)
void setAttribute(const QWebEngineSettings::WebAttribute &attribute)
@ SafeJsWorld
Definition: webpage.h:45
WebPage * page() const
Definition: webview.cpp:132
QString title(bool allowEmpty=false) const
Definition: webview.cpp:107
#define mApp
i
Definition: i18n.py:23
#define QL1S(x)
Definition: qzcommon.h:44
#define QSL(x)
Definition: qzcommon.h:40
const QList< QWebEngineSettings::WebAttribute > supportedAttribute
const QList< QWebEnginePage::Feature > supportedFeatures
QMap< QWebEnginePage::Feature, Permission > features
QMap< QWebEngineSettings::WebAttribute, Permission > attributes