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 &parameters, 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 &parameters, 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 &parameters) 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