File indexing completed on 2024-05-12 04:58:13

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 "siteinfo.h"
0019 #include "ui_siteinfo.h"
0020 #include "listitemdelegate.h"
0021 #include "webview.h"
0022 #include "webpage.h"
0023 #include "mainapplication.h"
0024 #include "downloaditem.h"
0025 #include "certificateinfowidget.h"
0026 #include "qztools.h"
0027 #include "iconprovider.h"
0028 #include "scripts.h"
0029 #include "networkmanager.h"
0030 #include "locationbar.h"
0031 
0032 #include <QMenu>
0033 #include <QMessageBox>
0034 #include <QFileDialog>
0035 #include <QNetworkDiskCache>
0036 #include <QClipboard>
0037 #include <QTimer>
0038 #include <QGraphicsPixmapItem>
0039 #include <QShortcut>
0040 
0041 SiteInfo::SiteInfo(WebView *view)
0042     : QDialog(view)
0043     , ui(new Ui::SiteInfo)
0044     , m_certWidget(nullptr)
0045     , m_view(view)
0046     , m_imageReply(nullptr)
0047     , m_baseUrl(view->url())
0048 {
0049     setAttribute(Qt::WA_DeleteOnClose);
0050     ui->setupUi(this);
0051     ui->treeTags->setLayoutDirection(Qt::LeftToRight);
0052     QzTools::centerWidgetOnScreen(this);
0053 
0054     auto* delegate = new ListItemDelegate(24, ui->listWidget);
0055     delegate->setUpdateParentHeight(true);
0056     delegate->setUniformItemSizes(true);
0057     ui->listWidget->setItemDelegate(delegate);
0058 
0059     ui->listWidget->item(0)->setIcon(QIcon::fromTheme(QSL("document-properties"), QIcon(QSL(":/icons/preferences/document-properties.png"))));
0060     ui->listWidget->item(1)->setIcon(QIcon::fromTheme(QSL("applications-graphics"), QIcon(QSL(":/icons/preferences/applications-graphics.png"))));
0061     ui->listWidget->item(0)->setSelected(true);
0062 
0063     // General
0064     ui->heading->setText(QSL("<b>%1</b>:").arg(m_view->title()));
0065     ui->siteAddress->setText(m_view->url().toString());
0066 
0067     if (m_view->url().scheme() == QL1S("https"))
0068         ui->securityLabel->setText(tr("<b>Connection is Encrypted.</b>"));
0069     else
0070         ui->securityLabel->setText(tr("<b>Connection Not Encrypted.</b>"));
0071 
0072     m_view->page()->runJavaScript(QSL("document.charset"), WebPage::SafeJsWorld, [this](const QVariant &res) {
0073         ui->encodingLabel->setText(res.toString());
0074     });
0075 
0076     // Meta
0077     m_view->page()->runJavaScript(Scripts::getAllMetaAttributes(), WebPage::SafeJsWorld, [this](const QVariant &res) {
0078         const QVariantList &list = res.toList();
0079         for (const QVariant &val : list) {
0080             const QVariantMap &meta = val.toMap();
0081             QString content = meta.value(QSL("content")).toString();
0082             QString name = meta.value(QSL("name")).toString();
0083 
0084             if (name.isEmpty())
0085                 name = meta.value(QSL("httpequiv")).toString();
0086 
0087             if (content.isEmpty() || name.isEmpty())
0088                 continue;
0089 
0090             auto* item = new QTreeWidgetItem(ui->treeTags);
0091             item->setText(0, name);
0092             item->setText(1, content);
0093             ui->treeTags->addTopLevelItem(item);
0094         }
0095     });
0096 
0097     // Images
0098     m_view->page()->runJavaScript(Scripts::getAllImages(), WebPage::SafeJsWorld, [this](const QVariant &res) {
0099         const QVariantList &list = res.toList();
0100         for (const QVariant &val : list) {
0101             const QVariantMap &img = val.toMap();
0102             QString src = img.value(QSL("src")).toString();
0103             QString alt = img.value(QSL("alt")).toString();
0104             if (alt.isEmpty()) {
0105                 if (src.indexOf(QLatin1Char('/')) == -1) {
0106                     alt = src;
0107                 }
0108                 else {
0109                     int pos = src.lastIndexOf(QLatin1Char('/'));
0110                     alt = src.mid(pos);
0111                     alt.remove(QLatin1Char('/'));
0112                 }
0113             }
0114 
0115             if (src.isEmpty() || alt.isEmpty())
0116                 continue;
0117 
0118             auto* item = new QTreeWidgetItem(ui->treeImages);
0119             item->setText(0, alt);
0120             item->setText(1, src);
0121             ui->treeImages->addTopLevelItem(item);
0122         }
0123     });
0124 
0125     connect(ui->saveButton, SIGNAL(clicked(QAbstractButton*)), this, SLOT(saveImage()));
0126     connect(ui->listWidget, SIGNAL(currentRowChanged(int)), ui->stackedWidget, SLOT(setCurrentIndex(int)));
0127     connect(ui->treeImages, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(showImagePreview(QTreeWidgetItem*)));
0128     connect(ui->treeImages, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(imagesCustomContextMenuRequested(QPoint)));
0129     connect(ui->treeTags, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(tagsCustomContextMenuRequested(QPoint)));
0130 
0131     auto *shortcutTagsCopyAll = new QShortcut(QKeySequence(QSL("Ctrl+C")), ui->treeTags);
0132     shortcutTagsCopyAll->setContext(Qt::WidgetShortcut);
0133     connect(shortcutTagsCopyAll, &QShortcut::activated, [=]{copySelectedItems(ui->treeTags, false);});
0134 
0135     auto *shortcutTagsCopyValues = new QShortcut(QKeySequence(QSL("Ctrl+Shift+C")), ui->treeTags);
0136     shortcutTagsCopyValues->setContext(Qt::WidgetShortcut);
0137     connect(shortcutTagsCopyValues, &QShortcut::activated, [=]{copySelectedItems(ui->treeTags, true);});
0138 
0139     auto *shortcutImagesCopyAll = new QShortcut(QKeySequence(QSL("Ctrl+C")), ui->treeImages);
0140     shortcutImagesCopyAll->setContext(Qt::WidgetShortcut);
0141     connect(shortcutImagesCopyAll, &QShortcut::activated, [=]{copySelectedItems(ui->treeImages, false);});
0142 
0143     auto *shortcutImagesCopyValues = new QShortcut(QKeySequence(QSL("Ctrl+Shift+C")), ui->treeImages);
0144     shortcutImagesCopyValues->setContext(Qt::WidgetShortcut);
0145     connect(shortcutImagesCopyValues, &QShortcut::activated, [=]{copySelectedItems(ui->treeImages, true);});
0146 
0147     ui->treeImages->setContextMenuPolicy(Qt::CustomContextMenu);
0148     ui->treeImages->sortByColumn(-1, Qt::AscendingOrder);
0149 
0150     ui->treeTags->setContextMenuPolicy(Qt::CustomContextMenu);
0151     ui->treeTags->sortByColumn(-1, Qt::AscendingOrder);
0152 
0153     QzTools::setWmClass(QSL("Site Info"), this);
0154 }
0155 
0156 bool SiteInfo::canShowSiteInfo(const QUrl &url)
0157 {
0158     if (LocationBar::convertUrlToText(url).isEmpty())
0159         return false;
0160 
0161     if (url.scheme() == QL1S("falkon") || url.scheme() == QL1S("view-source") || url.scheme() == QL1S("extension"))
0162         return false;
0163 
0164     return true;
0165 }
0166 
0167 void SiteInfo::imagesCustomContextMenuRequested(const QPoint &p)
0168 {
0169     QTreeWidgetItem* item = ui->treeImages->itemAt(p);
0170     if (!item) {
0171         return;
0172     }
0173 
0174     QMenu menu;
0175     menu.addAction(QIcon::fromTheme(QSL("edit-copy")), tr("Copy Image Location"), QKeySequence(QSL("Ctrl+C")), this, [=]{copySelectedItems(ui->treeImages, false);});
0176     menu.addAction(tr("Copy Image Name"), QKeySequence(QSL("Ctrl+Shift+C")), this, [=]{copySelectedItems(ui->treeImages, true);});
0177     menu.addSeparator();
0178     menu.addAction(QIcon::fromTheme(QSL("document-save")), tr("Save Image to Disk"), this, SLOT(saveImage()));
0179     menu.exec(ui->treeImages->viewport()->mapToGlobal(p));
0180 }
0181 
0182 void SiteInfo::tagsCustomContextMenuRequested(const QPoint &p)
0183 {
0184     QTreeWidgetItem* item = ui->treeTags->itemAt(p);
0185     if (!item) {
0186         return;
0187     }
0188 
0189     QMenu menu;
0190     menu.addAction(tr("Copy Values"), QKeySequence(QSL("Ctrl+C")), this, [=]{copySelectedItems(ui->treeTags, false);});
0191     menu.addAction(tr("Copy Tags and Values"), QKeySequence(QSL("Ctrl+Shift+C")), this, [=]{copySelectedItems(ui->treeTags, true);});
0192     menu.exec(ui->treeTags->viewport()->mapToGlobal(p));
0193 }
0194 
0195 void SiteInfo::copySelectedItems(const QTreeWidget* treeWidget, const bool both)
0196 {
0197     QList<QTreeWidgetItem*> itemList = treeWidget->selectedItems();
0198     QString tmpText = QSL("");
0199 
0200     for (int i = 0; i < itemList.size(); ++i) {
0201         if (i != 0) {
0202             tmpText.append(QSL("\n"));
0203         }
0204         if (both) {
0205             tmpText.append((itemList[i])->text(0));
0206             tmpText.append(QSL("\t"));
0207         }
0208         tmpText.append((itemList[i])->text(1));
0209     }
0210     qApp->clipboard()->setText(tmpText);
0211 }
0212 
0213 void SiteInfo::saveImage()
0214 {
0215     QTreeWidgetItem* item = ui->treeImages->currentItem();
0216     if (!item) {
0217         return;
0218     }
0219 
0220     if (!ui->mediaPreview->scene() || ui->mediaPreview->scene()->items().isEmpty())
0221         return;
0222 
0223     QGraphicsItem *graphicsItem = ui->mediaPreview->scene()->items().at(0);
0224     auto *pixmapItem = static_cast<QGraphicsPixmapItem*>(graphicsItem);
0225     if (graphicsItem->type() != QGraphicsPixmapItem::Type || !pixmapItem)
0226         return;
0227 
0228     if (!pixmapItem || pixmapItem->pixmap().isNull()) {
0229         QMessageBox::warning(this, tr("Error!"), tr("This preview is not available!"));
0230         return;
0231     }
0232 
0233     QString imageFileName = QzTools::getFileNameFromUrl(QUrl(item->text(1)));
0234     int index = imageFileName.lastIndexOf(QLatin1Char('.'));
0235     if (index != -1) {
0236         imageFileName.truncate(index);
0237         imageFileName.append(QL1S(".png"));
0238     }
0239 
0240     QString filePath = QzTools::getSaveFileName(QSL("SiteInfo-DownloadImage"), this, tr("Save image..."),
0241                                                 QDir::homePath() + QDir::separator() + imageFileName,
0242                                                 QSL("*.png"));
0243     if (filePath.isEmpty()) {
0244         return;
0245     }
0246 
0247     if (!pixmapItem->pixmap().save(filePath, "PNG")) {
0248         QMessageBox::critical(this, tr("Error!"), tr("Cannot write to file!"));
0249         return;
0250     }
0251 }
0252 
0253 void SiteInfo::showLoadingText()
0254 {
0255     delete ui->mediaPreview->scene();
0256     auto* scene = new QGraphicsScene(ui->mediaPreview);
0257 
0258     scene->addText(tr("Loading..."));
0259 
0260     ui->mediaPreview->setScene(scene);
0261 }
0262 
0263 void SiteInfo::showPixmap(QPixmap pixmap)
0264 {
0265     pixmap.setDevicePixelRatio(devicePixelRatioF());
0266 
0267     delete ui->mediaPreview->scene();
0268     auto* scene = new QGraphicsScene(ui->mediaPreview);
0269 
0270     if (pixmap.isNull())
0271         scene->addText(tr("Preview not available"));
0272     else
0273         scene->addPixmap(pixmap);
0274 
0275     ui->mediaPreview->setScene(scene);
0276 }
0277 
0278 void SiteInfo::showImagePreview(QTreeWidgetItem *item)
0279 {
0280     if ((!item) || (item->treeWidget()->selectedItems().length() > 1)) {
0281         return;
0282     }
0283     QUrl imageUrl = QUrl::fromEncoded(item->text(1).toUtf8());
0284     if (imageUrl.isRelative()) {
0285         imageUrl = m_baseUrl.resolved(imageUrl);
0286     }
0287 
0288     QPixmap pixmap;
0289     bool loading = false;
0290 
0291     if (imageUrl.scheme() == QLatin1String("data")) {
0292         QByteArray encodedUrl = item->text(1).toUtf8();
0293         QByteArray imageData = encodedUrl.mid(encodedUrl.indexOf(',') + 1);
0294         pixmap = QzTools::pixmapFromByteArray(imageData);
0295     }
0296     else if (imageUrl.scheme() == QLatin1String("file")) {
0297         pixmap = QPixmap(imageUrl.toLocalFile());
0298     }
0299     else if (imageUrl.scheme() == QLatin1String("qrc")) {
0300         pixmap = QPixmap(imageUrl.toString().mid(3)); // Remove qrc from url
0301     }
0302     else {
0303         delete m_imageReply;
0304         m_imageReply = mApp->networkManager()->get(QNetworkRequest(imageUrl));
0305         connect(m_imageReply, &QNetworkReply::finished, this, [this]() {
0306             if (m_imageReply->error() != QNetworkReply::NoError)
0307                 return;
0308 
0309             const QByteArray &data = m_imageReply->readAll();
0310             showPixmap(QPixmap::fromImage(QImage::fromData(data)));
0311         });
0312 
0313         loading = true;
0314         showLoadingText();
0315     }
0316 
0317     if (!loading)
0318         showPixmap(pixmap);
0319 }
0320 
0321 SiteInfo::~SiteInfo()
0322 {
0323     delete ui;
0324     delete m_certWidget;
0325 }