1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2017 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
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 **************************************************************************/
19
20 #include "PageInformationContentsWidget.h"
21 #include "../../../core/ThemesManager.h"
22 #include "../../../ui/Action.h"
23 #include "../../../ui/MainWindow.h"
24 #include "../../../ui/Window.h"
25
26 #include "ui_PageInformationContentsWidget.h"
27
28 #include <QtGui/QClipboard>
29
30 namespace Otter
31 {
32
PageInformationContentsWidget(const QVariantMap & parameters,QWidget * parent)33 PageInformationContentsWidget::PageInformationContentsWidget(const QVariantMap ¶meters, QWidget *parent) : ContentsWidget(parameters, nullptr, parent),
34 m_window(nullptr),
35 m_ui(new Ui::PageInformationContentsWidget)
36 {
37 m_ui->setupUi(this);
38 m_ui->filterLineEditWidget->setClearOnEscape(true);
39
40 const QVector<SectionName> sections({GeneralSection, SecuritySection, PermissionsSection, MetaSection, HeadersSection});
41 QStandardItemModel *model(new QStandardItemModel(this));
42 model->setHorizontalHeaderLabels({tr("Name"), tr("Value")});
43
44 for (int i = 0; i < sections.count(); ++i)
45 {
46 QStandardItem *item(new QStandardItem());
47 item->setData(sections.at(i), Qt::UserRole);
48
49 model->appendRow({item, new QStandardItem()});
50 }
51
52 m_ui->informationViewWidget->setViewMode(ItemViewWidget::TreeView);
53 m_ui->informationViewWidget->setModel(model);
54 m_ui->informationViewWidget->expandAll();
55
56 const MainWindow *mainWindow(MainWindow::findMainWindow(parentWidget()));
57
58 if (mainWindow)
59 {
60 m_window = mainWindow->getActiveWindow();
61
62 connect(mainWindow, &MainWindow::currentWindowChanged, this, [=]()
63 {
64 Window *window(mainWindow->getActiveWindow());
65
66 if (window != m_window)
67 {
68 if (m_window)
69 {
70 disconnect(m_window, &Window::loadingStateChanged, this, &PageInformationContentsWidget::updateSections);
71
72 if (m_window->getWebWidget())
73 {
74 m_window->getWebWidget()->stopWatchingChanges(this, WebWidget::MetaDataWatcher);
75
76 disconnect(m_window->getWebWidget(), &WebWidget::watchedDataChanged, this, &PageInformationContentsWidget::handleWatchedDataChanged);
77 }
78 }
79
80 m_window = window;
81
82 if (window)
83 {
84 connect(window, &Window::loadingStateChanged, this, &PageInformationContentsWidget::updateSections);
85
86 if (window->getWebWidget())
87 {
88 window->getWebWidget()->stopWatchingChanges(this, WebWidget::MetaDataWatcher);
89
90 connect(window->getWebWidget(), &WebWidget::watchedDataChanged, this, &PageInformationContentsWidget::handleWatchedDataChanged);
91 }
92 }
93 }
94
95 updateSections();
96 });
97 }
98
99 updateSections();
100
101 connect(m_ui->filterLineEditWidget, &LineEditWidget::textChanged, m_ui->informationViewWidget, &ItemViewWidget::setFilterString);
102 connect(m_ui->informationViewWidget, &ItemViewWidget::customContextMenuRequested, this, &PageInformationContentsWidget::showContextMenu);
103 connect(m_ui->informationViewWidget, &ItemViewWidget::needsActionsUpdate, this, [&]()
104 {
105 emit arbitraryActionsStateChanged({ActionsManager::CopyAction});
106 });
107 }
108
~PageInformationContentsWidget()109 PageInformationContentsWidget::~PageInformationContentsWidget()
110 {
111 delete m_ui;
112 }
113
changeEvent(QEvent * event)114 void PageInformationContentsWidget::changeEvent(QEvent *event)
115 {
116 ContentsWidget::changeEvent(event);
117
118 if (event->type() == QEvent::LanguageChange)
119 {
120 m_ui->retranslateUi(this);
121
122 if (m_ui->informationViewWidget->getSourceModel())
123 {
124 m_ui->informationViewWidget->getSourceModel()->setHorizontalHeaderLabels({tr("Name"), tr("Value")});
125 }
126
127 updateSections();
128 }
129 }
130
triggerAction(int identifier,const QVariantMap & parameters,ActionsManager::TriggerType trigger)131 void PageInformationContentsWidget::triggerAction(int identifier, const QVariantMap ¶meters, ActionsManager::TriggerType trigger)
132 {
133 if (identifier == ActionsManager::CopyAction)
134 {
135 const QModelIndex index(m_ui->informationViewWidget->getCurrentIndex());
136
137 if (index.isValid() && index.parent().isValid())
138 {
139 QGuiApplication::clipboard()->setText(index.data(Qt::ToolTipRole).toString());
140 }
141 }
142 else
143 {
144 ContentsWidget::triggerAction(identifier, parameters, trigger);
145 }
146 }
147
addEntry(QStandardItem * parent,const QString & label,const QString & value)148 void PageInformationContentsWidget::addEntry(QStandardItem *parent, const QString &label, const QString &value)
149 {
150 const QString toolTip(label + QLatin1String(": ") + (value.isEmpty() ? tr("<empty>") : value));
151 QList<QStandardItem*> items({new QStandardItem(label), new QStandardItem(value.isEmpty() ? tr("<empty>") : value)});
152 items[0]->setToolTip(toolTip);
153 items[0]->setFlags(items[0]->flags() | Qt::ItemNeverHasChildren);
154 items[1]->setToolTip(toolTip);
155 items[1]->setFlags(items[1]->flags() | Qt::ItemNeverHasChildren);
156
157 parent->appendRow(items);
158 }
159
updateSections()160 void PageInformationContentsWidget::updateSections()
161 {
162 for (int i = 0; i < m_ui->informationViewWidget->getRowCount(); ++i)
163 {
164 const QModelIndex index(m_ui->informationViewWidget->getIndex(i));
165 QStandardItem *sectionItem(m_ui->informationViewWidget->getItem(index));
166 QStandardItemModel *model(m_ui->informationViewWidget->getSourceModel());
167 model->removeRows(0, model->rowCount(index), index);
168
169 switch (static_cast<SectionName>(index.data(Qt::UserRole).toInt()))
170 {
171 case GeneralSection:
172 m_ui->informationViewWidget->setData(index, tr("General"), Qt::DisplayRole);
173
174 if (sectionItem)
175 {
176 const bool canGetPageInformation(m_window && m_window->getWebWidget() && m_window->getLoadingState() == WebWidget::FinishedLoadingState);
177
178 addEntry(sectionItem, tr("Title"), (m_window ? m_window->getTitle() : QString()));
179
180 if (!m_window || m_window->getUrl().scheme() != QLatin1String("about"))
181 {
182 addEntry(sectionItem, tr("MIME type"), (canGetPageInformation ? m_window->getWebWidget()->getPageInformation(WebWidget::DocumentMimeTypeInformation).toString() : QString()));
183 addEntry(sectionItem, tr("Document size"), (canGetPageInformation ? Utils::formatUnit(m_window->getWebWidget()->getPageInformation(WebWidget::DocumentBytesTotalInformation).toLongLong(), false, 1, true) : QString()));
184 addEntry(sectionItem, tr("Total size"), (canGetPageInformation ? Utils::formatUnit(m_window->getWebWidget()->getPageInformation(WebWidget::TotalBytesTotalInformation).toLongLong(), false, 1, true) : QString()));
185
186 if (canGetPageInformation && m_window->getWebWidget()->getPageInformation(WebWidget::RequestsBlockedInformation).toInt() > 0)
187 {
188 addEntry(sectionItem, tr("Number of requests"), tr("%1 (%n blocked)", "", m_window->getWebWidget()->getPageInformation(WebWidget::RequestsBlockedInformation).toInt()).arg(m_window->getWebWidget()->getPageInformation(WebWidget::RequestsFinishedInformation).toInt()));
189 }
190 else
191 {
192 addEntry(sectionItem, tr("Number of requests"), (canGetPageInformation ? QString::number(m_window->getWebWidget()->getPageInformation(WebWidget::RequestsFinishedInformation).toInt()) : QString()));
193 }
194
195 addEntry(sectionItem, tr("Downloaded"), (canGetPageInformation ? Utils::formatDateTime(m_window->getWebWidget()->getPageInformation(WebWidget::LoadingFinishedInformation).toDateTime(), {}, false) : QString()));
196 }
197 }
198
199 break;
200 case HeadersSection:
201 m_ui->informationViewWidget->setData(index, tr("Headers"), Qt::DisplayRole);
202
203 if (sectionItem && m_window && m_window->getWebWidget())
204 {
205 const QMap<QByteArray, QByteArray> headers(m_window->getWebWidget()->getHeaders());
206 QMap<QByteArray, QByteArray>::const_iterator iterator;
207
208 for (iterator = headers.constBegin(); iterator != headers.constEnd(); ++iterator)
209 {
210 addEntry(sectionItem, QString(iterator.key()), QString(iterator.value()));
211 }
212 }
213
214 break;
215 case MetaSection:
216 m_ui->informationViewWidget->setData(index, tr("Meta"), Qt::DisplayRole);
217
218 if (sectionItem && m_window && m_window->getWebWidget())
219 {
220 const QMultiMap<QString, QString> metaData(m_window->getWebWidget()->getMetaData());
221 QMultiMap<QString, QString>::const_iterator iterator;
222
223 for (iterator = metaData.constBegin(); iterator != metaData.constEnd(); ++iterator)
224 {
225 if (!iterator.key().isEmpty())
226 {
227 addEntry(sectionItem, iterator.key(), iterator.value());
228 }
229 }
230 }
231
232 break;
233 case PermissionsSection:
234 m_ui->informationViewWidget->setData(index, tr("Permissions"), Qt::DisplayRole);
235
236 break;
237 case SecuritySection:
238 m_ui->informationViewWidget->setData(index, tr("Security"), Qt::DisplayRole);
239
240 if (sectionItem && m_window && m_window->getWebWidget())
241 {
242 const QSslCipher cipher(m_window->getWebWidget()->getSslInformation().cipher);
243
244 if (!cipher.isNull())
245 {
246 addEntry(sectionItem, tr("Cipher protocol"), cipher.protocolString());
247 addEntry(sectionItem, tr("Cipher authentication method"), cipher.authenticationMethod());
248 addEntry(sectionItem, tr("Cipher encryption method"), cipher.encryptionMethod());
249 addEntry(sectionItem, tr("Cipher key exchange method"), cipher.keyExchangeMethod());
250 }
251 }
252
253 break;
254 default:
255 break;
256 }
257
258 m_ui->informationViewWidget->setRowHidden(i, model->invisibleRootItem()->index(), (model->rowCount(index) == 0));
259 }
260 }
261
handleWatchedDataChanged(WebWidget::ChangeWatcher watcher)262 void PageInformationContentsWidget::handleWatchedDataChanged(WebWidget::ChangeWatcher watcher)
263 {
264 if (watcher == WebWidget::MetaDataWatcher)
265 {
266 updateSections();
267 }
268 }
269
showContextMenu(const QPoint & position)270 void PageInformationContentsWidget::showContextMenu(const QPoint &position)
271 {
272 const QModelIndex index(m_ui->informationViewWidget->indexAt(position));
273
274 if (index.isValid())
275 {
276 QMenu menu(this);
277 menu.addAction(new Action(ActionsManager::CopyAction, {}, ActionExecutor::Object(this, this), &menu));
278 menu.exec(m_ui->informationViewWidget->mapToGlobal(position));
279 }
280 }
281
getTitle() const282 QString PageInformationContentsWidget::getTitle() const
283 {
284 return tr("Page Information");
285 }
286
getType() const287 QLatin1String PageInformationContentsWidget::getType() const
288 {
289 return QLatin1String("pageInformation");
290 }
291
getUrl() const292 QUrl PageInformationContentsWidget::getUrl() const
293 {
294 return {};
295 }
296
getIcon() const297 QIcon PageInformationContentsWidget::getIcon() const
298 {
299 return ThemesManager::createIcon(QLatin1String("view-information"), false);
300 }
301
getActionState(int identifier,const QVariantMap & parameters) const302 ActionsManager::ActionDefinition::State PageInformationContentsWidget::getActionState(int identifier, const QVariantMap ¶meters) const
303 {
304 if (identifier == ActionsManager::CopyAction)
305 {
306 const QModelIndex index(m_ui->informationViewWidget->getCurrentIndex());
307 ActionsManager::ActionDefinition::State state(ActionsManager::getActionDefinition(ActionsManager::CopyAction).getDefaultState());
308 state.isEnabled = (index.isValid() && index.parent().isValid());
309
310 return state;
311 }
312
313 return ContentsWidget::getActionState(identifier, parameters);
314 }
315
316 }
317