1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2013 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 * Copyright (C) 2015 - 2016 Jan Bajer aka bajasoft <jbajer@gmail.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **************************************************************************/
20 
21 #include "QtWebKitWebWidget.h"
22 #include "QtWebKitInspector.h"
23 #include "QtWebKitNetworkManager.h"
24 #include "QtWebKitPage.h"
25 #include "QtWebKitPluginWidget.h"
26 #include "QtWebKitWebBackend.h"
27 #include "../../../../core/Application.h"
28 #include "../../../../core/BookmarksManager.h"
29 #include "../../../../core/Console.h"
30 #include "../../../../core/CookieJar.h"
31 #include "../../../../core/ContentFiltersManager.h"
32 #include "../../../../core/GesturesManager.h"
33 #include "../../../../core/HistoryManager.h"
34 #include "../../../../core/JsonSettings.h"
35 #include "../../../../core/NetworkCache.h"
36 #include "../../../../core/NetworkManager.h"
37 #include "../../../../core/NetworkManagerFactory.h"
38 #include "../../../../core/NotesManager.h"
39 #include "../../../../core/SearchEnginesManager.h"
40 #include "../../../../core/SessionsManager.h"
41 #include "../../../../core/SettingsManager.h"
42 #include "../../../../core/ThemesManager.h"
43 #include "../../../../core/TransfersManager.h"
44 #include "../../../../core/Utils.h"
45 #include "../../../../ui/ContentsDialog.h"
46 #include "../../../../ui/ContentsWidget.h"
47 #include "../../../../ui/ImagePropertiesDialog.h"
48 #include "../../../../ui/MainWindow.h"
49 #include "../../../../ui/SearchEnginePropertiesDialog.h"
50 #include "../../../../ui/SourceViewerWebWidget.h"
51 #include "../../../../ui/WebsitePreferencesDialog.h"
52 
53 #include <QtCore/QDataStream>
54 #include <QtCore/QFileInfo>
55 #include <QtCore/QJsonArray>
56 #include <QtCore/QJsonDocument>
57 #include <QtCore/QJsonObject>
58 #include <QtCore/QMimeData>
59 #include <QtCore/QTimer>
60 #include <QtCore/QUuid>
61 #include <QtGui/QClipboard>
62 #include <QtGui/QImageWriter>
63 #include <QtGui/QMouseEvent>
64 #include <QtPrintSupport/QPrintPreviewDialog>
65 #include <QtWebKit/QWebFullScreenRequest>
66 #include <QtWebKit/QWebElement>
67 #include <QtWebKit/QWebHistory>
68 #include <QtWebKitWidgets/QWebFrame>
69 #include <QtWidgets/QMenu>
70 #include <QtWidgets/QMessageBox>
71 #include <QtWidgets/QShortcut>
72 #include <QtWidgets/QUndoStack>
73 #include <QtWidgets/QVBoxLayout>
74 
75 namespace Otter
76 {
77 
QtWebKitWebWidget(const QVariantMap & parameters,WebBackend * backend,QtWebKitNetworkManager * networkManager,ContentsWidget * parent)78 QtWebKitWebWidget::QtWebKitWebWidget(const QVariantMap &parameters, WebBackend *backend, QtWebKitNetworkManager *networkManager, ContentsWidget *parent) : WebWidget(parameters, backend, parent),
79 	m_webView(new QWebView(this)),
80 	m_page(nullptr),
81 	m_inspector(nullptr),
82 	m_networkManager(networkManager),
83 	m_loadingState(FinishedLoadingState),
84 	m_amountOfDeferredPlugins(0),
85 	m_transfersTimer(0),
86 	m_canLoadPlugins(false),
87 	m_isAudioMuted(false),
88 	m_isFullScreen(false),
89 	m_isTyped(false),
90 	m_isNavigating(false)
91 {
92 	const bool isPrivate(SessionsManager::calculateOpenHints(parameters).testFlag(SessionsManager::PrivateOpen));
93 	QVBoxLayout *layout(new QVBoxLayout(this));
94 	layout->addWidget(m_webView);
95 	layout->setContentsMargins(0, 0, 0, 0);
96 
97 	setLayout(layout);
98 	setFocusPolicy(Qt::StrongFocus);
99 
100 	if (m_networkManager)
101 	{
102 		m_networkManager->setWidget(this);
103 	}
104 	else
105 	{
106 		m_networkManager = new QtWebKitNetworkManager(isPrivate, nullptr, this);
107 	}
108 
109 	m_page = new QtWebKitPage(m_networkManager, this);
110 	m_page->settings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, isPrivate);
111 	m_page->setParent(m_webView);
112 	m_page->setVisibilityState(isVisible() ? QWebPage::VisibilityStateVisible : QWebPage::VisibilityStateHidden);
113 
114 	m_webView->setPage(m_page);
115 	m_webView->setContextMenuPolicy(Qt::CustomContextMenu);
116 	m_webView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
117 	m_webView->installEventFilter(this);
118 
119 	if (parameters.contains(QLatin1String("size")))
120 	{
121 		m_page->setViewportSize(parameters[QLatin1String("size")].toSize());
122 	}
123 
124 	handleOptionChanged(SettingsManager::Permissions_ScriptsCanShowStatusMessagesOption, SettingsManager::getOption(SettingsManager::Permissions_ScriptsCanShowStatusMessagesOption));
125 	handleOptionChanged(SettingsManager::Content_BackgroundColorOption, SettingsManager::getOption(SettingsManager::Content_BackgroundColorOption));
126 	handleOptionChanged(SettingsManager::History_BrowsingLimitAmountWindowOption, SettingsManager::getOption(SettingsManager::History_BrowsingLimitAmountWindowOption));
127 	setZoom(SettingsManager::getOption(SettingsManager::Content_DefaultZoomOption).toInt());
128 
129 	connect(SettingsManager::getInstance(), &SettingsManager::optionChanged, this, &QtWebKitWebWidget::handleOptionChanged);
130 	connect(m_page, &QtWebKitPage::requestedNewWindow, this, &QtWebKitWebWidget::requestedNewWindow);
131 	connect(m_page, &QtWebKitPage::requestedPopupWindow, this, &QtWebKitWebWidget::requestedPopupWindow);
132 	connect(m_page, &QtWebKitPage::saveFrameStateRequested, this, &QtWebKitWebWidget::saveState);
133 	connect(m_page, &QtWebKitPage::restoreFrameStateRequested, this, &QtWebKitWebWidget::restoreState);
134 	connect(m_page, &QtWebKitPage::downloadRequested, this, &QtWebKitWebWidget::handleDownloadRequested);
135 	connect(m_page, &QtWebKitPage::unsupportedContent, this, &QtWebKitWebWidget::handleUnsupportedContent);
136 	connect(m_page, &QtWebKitPage::linkHovered, this, &QtWebKitWebWidget::setStatusMessageOverride);
137 	connect(m_page, &QtWebKitPage::microFocusChanged, [&]()
138 	{
139 		emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::EditingCategory});
140 	});
141 	connect(m_page, &QtWebKitPage::printRequested, this, &QtWebKitWebWidget::handlePrintRequest);
142 	connect(m_page, &QtWebKitPage::windowCloseRequested, this, &QtWebKitWebWidget::handleWindowCloseRequest);
143 	connect(m_page, &QtWebKitPage::fullScreenRequested, this, &QtWebKitWebWidget::handleFullScreenRequest);
144 	connect(m_page, &QtWebKitPage::featurePermissionRequested, this, &QtWebKitWebWidget::handlePermissionRequest);
145 	connect(m_page, &QtWebKitPage::featurePermissionRequestCanceled, this, &QtWebKitWebWidget::handlePermissionCancel);
146 	connect(m_page, &QtWebKitPage::loadStarted, this, &QtWebKitWebWidget::handleLoadStarted);
147 	connect(m_page, &QtWebKitPage::loadProgress, this, &QtWebKitWebWidget::handleLoadProgress);
148 	connect(m_page, &QtWebKitPage::loadFinished, this, &QtWebKitWebWidget::handleLoadFinished);
149 	connect(m_page, &QtWebKitPage::recentlyAudibleChanged, this, &QtWebKitWebWidget::isAudibleChanged);
150 	connect(m_page->mainFrame(), &QWebFrame::titleChanged, this, &QtWebKitWebWidget::notifyTitleChanged);
151 	connect(m_page->mainFrame(), &QWebFrame::urlChanged, this, &QtWebKitWebWidget::notifyUrlChanged);
152 	connect(m_page->mainFrame(), &QWebFrame::iconChanged, this, &QtWebKitWebWidget::notifyIconChanged);
153 	connect(m_page->mainFrame(), &QWebFrame::contentsSizeChanged, this, &QtWebKitWebWidget::geometryChanged);
154 	connect(m_page->mainFrame(), &QWebFrame::initialLayoutCompleted, this, &QtWebKitWebWidget::geometryChanged);
155 	connect(m_page->undoStack(), &QUndoStack::canRedoChanged, this, &QtWebKitWebWidget::notifyRedoActionStateChanged);
156 	connect(m_page->undoStack(), &QUndoStack::redoTextChanged, this, &QtWebKitWebWidget::notifyRedoActionStateChanged);
157 	connect(m_page->undoStack(), &QUndoStack::canUndoChanged, this, &QtWebKitWebWidget::notifyUndoActionStateChanged);
158 	connect(m_page->undoStack(), &QUndoStack::undoTextChanged, this, &QtWebKitWebWidget::notifyUndoActionStateChanged);
159 	connect(m_networkManager, &QtWebKitNetworkManager::pageInformationChanged, this, &QtWebKitWebWidget::pageInformationChanged);
160 	connect(m_networkManager, &QtWebKitNetworkManager::requestBlocked, this, &QtWebKitWebWidget::requestBlocked);
161 	connect(m_networkManager, &QtWebKitNetworkManager::contentStateChanged, this, [&]()
162 	{
163 		emit contentStateChanged(getContentState());
164 	});
165 	connect(new QShortcut(QKeySequence(QKeySequence::SelectAll), this, nullptr, nullptr, Qt::WidgetWithChildrenShortcut), &QShortcut::activated, [&]()
166 	{
167 		triggerAction(ActionsManager::SelectAllAction);
168 	});
169 }
170 
~QtWebKitWebWidget()171 QtWebKitWebWidget::~QtWebKitWebWidget()
172 {
173 	m_networkManager->blockSignals(true);
174 
175 	m_page->undoStack()->blockSignals(true);
176 	m_page->blockSignals(true);
177 	m_page->triggerAction(QWebPage::Stop);
178 	m_page->settings()->setAttribute(QWebSettings::JavascriptEnabled, false);
179 }
180 
timerEvent(QTimerEvent * event)181 void QtWebKitWebWidget::timerEvent(QTimerEvent *event)
182 {
183 	if (event->timerId() == m_transfersTimer)
184 	{
185 		killTimer(m_transfersTimer);
186 
187 		Transfer *transfer(m_transfers.dequeue());
188 
189 		if (transfer)
190 		{
191 			startTransfer(transfer);
192 		}
193 
194 		if (m_transfers.isEmpty())
195 		{
196 			m_transfersTimer = 0;
197 		}
198 		else
199 		{
200 			m_transfersTimer = startTimer(250);
201 		}
202 	}
203 	else
204 	{
205 		WebWidget::timerEvent(event);
206 	}
207 }
208 
showEvent(QShowEvent * event)209 void QtWebKitWebWidget::showEvent(QShowEvent *event)
210 {
211 	WebWidget::showEvent(event);
212 
213 	m_page->setVisibilityState(QWebPage::VisibilityStateVisible);
214 }
215 
hideEvent(QHideEvent * event)216 void QtWebKitWebWidget::hideEvent(QHideEvent *event)
217 {
218 	WebWidget::hideEvent(event);
219 
220 	m_page->setVisibilityState(QWebPage::VisibilityStateHidden);
221 }
222 
focusInEvent(QFocusEvent * event)223 void QtWebKitWebWidget::focusInEvent(QFocusEvent *event)
224 {
225 	WebWidget::focusInEvent(event);
226 
227 	m_webView->setFocus();
228 
229 	emit widgetActivated(this);
230 }
231 
search(const QString & query,const QString & searchEngine)232 void QtWebKitWebWidget::search(const QString &query, const QString &searchEngine)
233 {
234 	QNetworkRequest request;
235 	QNetworkAccessManager::Operation method;
236 	QByteArray body;
237 
238 	if (SearchEnginesManager::setupSearchQuery(query, searchEngine, &request, &method, &body))
239 	{
240 		setRequestedUrl(request.url(), false, true);
241 		updateOptions(request.url());
242 
243 		m_page->mainFrame()->load(request, method, body);
244 	}
245 }
246 
print(QPrinter * printer)247 void QtWebKitWebWidget::print(QPrinter *printer)
248 {
249 	m_page->mainFrame()->print(printer);
250 }
251 
saveState(QWebFrame * frame,QWebHistoryItem * item)252 void QtWebKitWebWidget::saveState(QWebFrame *frame, QWebHistoryItem *item)
253 {
254 	if (frame == m_page->mainFrame())
255 	{
256 		QVariantList state(m_page->history()->currentItem().userData().toList());
257 
258 		if (state.isEmpty() || state.count() < 4)
259 		{
260 			state = {0, getZoom(), m_page->mainFrame()->scrollPosition(), QDateTime::currentDateTimeUtc()};
261 		}
262 		else
263 		{
264 			state[ZoomEntryData] = getZoom();
265 			state[PositionEntryData] = m_page->mainFrame()->scrollPosition();
266 		}
267 
268 		item->setUserData(state);
269 	}
270 }
271 
restoreState(QWebFrame * frame)272 void QtWebKitWebWidget::restoreState(QWebFrame *frame)
273 {
274 	if (frame == m_page->mainFrame())
275 	{
276 		const QVariantList state(m_page->history()->currentItem().userData().toList());
277 
278 		setZoom(state.value(ZoomEntryData, getZoom()).toInt());
279 
280 		if (m_page->mainFrame()->scrollPosition().isNull())
281 		{
282 			m_page->mainFrame()->setScrollPosition(state.value(PositionEntryData).toPoint());
283 		}
284 	}
285 }
286 
clearPluginToken()287 void QtWebKitWebWidget::clearPluginToken()
288 {
289 	QList<QWebFrame*> frames({m_page->mainFrame()});
290 
291 	while (!frames.isEmpty())
292 	{
293 		const QWebFrame *frame(frames.takeFirst());
294 		QWebElement element(frame->documentElement().findFirst(QStringLiteral("object[data-otter-browser='%1'], embed[data-otter-browser='%1']").arg(m_pluginToken)));
295 
296 		if (!element.isNull())
297 		{
298 			element.removeAttribute(QLatin1String("data-otter-browser"));
299 
300 			break;
301 		}
302 
303 		frames.append(frame->childFrames());
304 	}
305 
306 	emit arbitraryActionsStateChanged({ActionsManager::LoadPluginsAction});
307 
308 	m_pluginToken.clear();
309 }
310 
resetSpellCheck(QWebElement element)311 void QtWebKitWebWidget::resetSpellCheck(QWebElement element)
312 {
313 	if (element.isNull())
314 	{
315 		element = m_page->mainFrame()->findFirstElement(QLatin1String("*:focus"));
316 	}
317 
318 	if (!element.isNull())
319 	{
320 		m_page->runScript(QLatin1String("resetSpellCheck"), element);
321 	}
322 }
323 
muteAudio(QWebFrame * frame,bool isMuted)324 void QtWebKitWebWidget::muteAudio(QWebFrame *frame, bool isMuted)
325 {
326 	if (!frame)
327 	{
328 		return;
329 	}
330 
331 	const QString script(QLatin1String("this.muted = ") + (isMuted ? QLatin1String("true") : QLatin1String("false")));
332 	const QWebElementCollection elements(frame->findAllElements(QLatin1String("audio, video")));
333 
334 	for (int i = 0; i < elements.count(); ++i)
335 	{
336 		elements.at(i).evaluateJavaScript(script);
337 	}
338 
339 	const QList<QWebFrame*> frames(frame->childFrames());
340 
341 	for (int i = 0; i < frames.count(); ++i)
342 	{
343 		muteAudio(frames.at(i), isMuted);
344 	}
345 }
346 
openRequest(const QNetworkRequest & request,QNetworkAccessManager::Operation operation,QIODevice * outgoingData)347 void QtWebKitWebWidget::openRequest(const QNetworkRequest &request, QNetworkAccessManager::Operation operation, QIODevice *outgoingData)
348 {
349 	m_formRequest = request;
350 	m_formRequestOperation = operation;
351 	m_formRequestBody = (outgoingData ? outgoingData->readAll() : QByteArray());
352 
353 	if (outgoingData)
354 	{
355 		outgoingData->close();
356 		outgoingData->deleteLater();
357 	}
358 
359 	setRequestedUrl(m_formRequest.url(), false, true);
360 	updateOptions(m_formRequest.url());
361 
362 	QTimer::singleShot(50, this, [&]()
363 	{
364 		m_page->mainFrame()->load(m_formRequest, m_formRequestOperation, m_formRequestBody);
365 
366 		m_formRequest = QNetworkRequest();
367 		m_formRequestBody = QByteArray();
368 	});
369 }
370 
openFormRequest(const QNetworkRequest & request,QNetworkAccessManager::Operation operation,QIODevice * outgoingData)371 void QtWebKitWebWidget::openFormRequest(const QNetworkRequest &request, QNetworkAccessManager::Operation operation, QIODevice *outgoingData)
372 {
373 	m_page->triggerAction(QWebPage::Stop);
374 
375 	QtWebKitWebWidget *widget(qobject_cast<QtWebKitWebWidget*>(clone(false)));
376 	widget->openRequest(request, operation, outgoingData);
377 
378 	emit requestedNewWindow(widget, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen), {});
379 }
380 
startDelayedTransfer(Transfer * transfer)381 void QtWebKitWebWidget::startDelayedTransfer(Transfer *transfer)
382 {
383 	m_transfers.enqueue(transfer);
384 
385 	if (m_transfersTimer == 0)
386 	{
387 		m_transfersTimer = startTimer(250);
388 	}
389 }
390 
handleDownloadRequested(const QNetworkRequest & request)391 void QtWebKitWebWidget::handleDownloadRequested(const QNetworkRequest &request)
392 {
393 	if ((!getCurrentHitTestResult().imageUrl.isEmpty() && request.url() == getCurrentHitTestResult().imageUrl) || (!getCurrentHitTestResult().mediaUrl.isEmpty() && request.url() == getCurrentHitTestResult().mediaUrl))
394 	{
395 		NetworkCache *cache(NetworkManagerFactory::getCache());
396 
397 		if (cache && cache->metaData(request.url()).isValid())
398 		{
399 			QIODevice *device(cache->data(request.url()));
400 
401 			if (device && device->size() > 0)
402 			{
403 				const QString path(Utils::getSavePath(request.url().fileName()).path);
404 
405 				if (path.isEmpty())
406 				{
407 					device->deleteLater();
408 
409 					return;
410 				}
411 
412 				QFile file(path);
413 
414 				if (!file.open(QIODevice::WriteOnly))
415 				{
416 					QMessageBox::critical(this, tr("Error"), tr("Failed to open file for writing."), QMessageBox::Close);
417 				}
418 
419 				file.write(device->readAll());
420 				file.close();
421 
422 				device->deleteLater();
423 
424 				return;
425 			}
426 
427 			if (device)
428 			{
429 				device->deleteLater();
430 			}
431 		}
432 		else if (!getCurrentHitTestResult().imageUrl.isEmpty() && getCurrentHitTestResult().imageUrl.url().contains(QLatin1String(";base64,")))
433 		{
434 			const QString imageUrl(getCurrentHitTestResult().imageUrl.url());
435 			const QString imageType(imageUrl.mid(11, (imageUrl.indexOf(QLatin1Char(';')) - 11)));
436 			const QString path(Utils::getSavePath(tr("file") + QLatin1Char('.') + imageType).path);
437 
438 			if (!path.isEmpty())
439 			{
440 				QImageWriter writer(path);
441 
442 				if (!writer.write(QImage::fromData(QByteArray::fromBase64(imageUrl.mid(imageUrl.indexOf(QLatin1String(";base64,")) + 7).toUtf8()), imageType.toStdString().c_str())))
443 				{
444 					Console::addMessage(tr("Failed to save image: %1").arg(writer.errorString()), Console::OtherCategory, Console::ErrorLevel, path, -1, getWindowIdentifier());
445 				}
446 			}
447 
448 			return;
449 		}
450 
451 		QNetworkRequest mutableRequest(request);
452 		mutableRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
453 
454 		new Transfer(m_networkManager->get(mutableRequest), {}, (Transfer::CanAskForPathOption | Transfer::CanAutoDeleteOption | Transfer::IsPrivateOption));
455 	}
456 	else
457 	{
458 		startDelayedTransfer(new Transfer(request, {}, (Transfer::CanNotifyOption | (isPrivate() ? Transfer::IsPrivateOption : Transfer::NoOption))));
459 	}
460 }
461 
handleUnsupportedContent(QNetworkReply * reply)462 void QtWebKitWebWidget::handleUnsupportedContent(QNetworkReply *reply)
463 {
464 	m_networkManager->registerTransfer(reply);
465 
466 	startDelayedTransfer(new Transfer(reply, {}, (Transfer::CanNotifyOption | (isPrivate() ? Transfer::IsPrivateOption : Transfer::NoOption))));
467 }
468 
handleOptionChanged(int identifier,const QVariant & value)469 void QtWebKitWebWidget::handleOptionChanged(int identifier, const QVariant &value)
470 {
471 	switch (identifier)
472 	{
473 		case SettingsManager::Content_BackgroundColorOption:
474 			{
475 				QPalette palette(m_page->palette());
476 				palette.setColor(QPalette::Base, QColor(value.toString()));
477 
478 				m_page->setPalette(palette);
479 			}
480 
481 			break;
482 		case SettingsManager::History_BrowsingLimitAmountWindowOption:
483 			m_page->history()->setMaximumItemCount(value.toInt());
484 
485 			break;
486 		case SettingsManager::Permissions_ScriptsCanShowStatusMessagesOption:
487 			disconnect(m_page, &QtWebKitPage::statusBarMessage, this, &QtWebKitWebWidget::setStatusMessage);
488 
489 			if (value.toBool() || SettingsManager::getOption(identifier, Utils::extractHost(getUrl())).toBool())
490 			{
491 				connect(m_page, &QtWebKitPage::statusBarMessage, this, &QtWebKitWebWidget::setStatusMessage);
492 			}
493 			else
494 			{
495 				setStatusMessage({});
496 			}
497 
498 			break;
499 		default:
500 			break;
501 	}
502 }
503 
handleLoadStarted()504 void QtWebKitWebWidget::handleLoadStarted()
505 {
506 	if (m_loadingState == OngoingLoadingState)
507 	{
508 		return;
509 	}
510 
511 	m_thumbnail = {};
512 	m_messageToken = QUuid::createUuid().toString();
513 	m_canLoadPlugins = (getOption(SettingsManager::Permissions_EnablePluginsOption, getUrl()).toString() == QLatin1String("enabled"));
514 	m_loadingState = OngoingLoadingState;
515 
516 	setStatusMessage({});
517 	setStatusMessageOverride({});
518 
519 	emit geometryChanged();
520 	emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::NavigationCategory});
521 	emit loadingStateChanged(OngoingLoadingState);
522 }
523 
handleLoadProgress(int progress)524 void QtWebKitWebWidget::handleLoadProgress(int progress)
525 {
526 	m_networkManager->setPageInformation(TotalLoadingProgressInformation, progress);
527 }
528 
handleLoadFinished(bool result)529 void QtWebKitWebWidget::handleLoadFinished(bool result)
530 {
531 	if (m_isAudioMuted)
532 	{
533 		muteAudio(m_page->mainFrame(), true);
534 	}
535 
536 	if (m_loadingState != OngoingLoadingState)
537 	{
538 		return;
539 	}
540 
541 	m_networkManager->handleLoadFinished(result);
542 
543 	m_thumbnail = {};
544 	m_loadingState = FinishedLoadingState;
545 
546 	updateAmountOfDeferredPlugins();
547 	handleHistory();
548 	startReloadTimer();
549 
550 	emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::NavigationCategory});
551 	emit contentStateChanged(getContentState());
552 	emit loadingStateChanged(FinishedLoadingState);
553 	emit watchedDataChanged(FeedsWatcher);
554 	emit watchedDataChanged(LinksWatcher);
555 	emit watchedDataChanged(MetaDataWatcher);
556 	emit watchedDataChanged(SearchEnginesWatcher);
557 	emit watchedDataChanged(StylesheetsWatcher);
558 }
559 
handleViewSourceReplyFinished()560 void QtWebKitWebWidget::handleViewSourceReplyFinished()
561 {
562 	QNetworkReply *reply(qobject_cast<QNetworkReply*>(sender()));
563 
564 	if (reply)
565 	{
566 		if (reply->error() == QNetworkReply::NoError && m_viewSourceReplies.contains(reply) && m_viewSourceReplies[reply])
567 		{
568 			m_viewSourceReplies[reply]->setContents(reply->readAll(), reply->header(QNetworkRequest::ContentTypeHeader).toString());
569 		}
570 
571 		m_viewSourceReplies.remove(reply);
572 
573 		reply->deleteLater();
574 	}
575 }
576 
handlePrintRequest(QWebFrame * frame)577 void QtWebKitWebWidget::handlePrintRequest(QWebFrame *frame)
578 {
579 	QPrintPreviewDialog printPreviewDialog(this);
580 	printPreviewDialog.setWindowFlags(printPreviewDialog.windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint);
581 	printPreviewDialog.setWindowTitle(tr("Print Preview"));
582 
583 	if (Application::getActiveWindow())
584 	{
585 		printPreviewDialog.resize(Application::getActiveWindow()->size());
586 	}
587 
588 	connect(&printPreviewDialog, &QPrintPreviewDialog::paintRequested, frame, &QWebFrame::print);
589 
590 	printPreviewDialog.exec();
591 }
592 
handleHistory()593 void QtWebKitWebWidget::handleHistory()
594 {
595 	if (isPrivate() || m_page->history()->count() == 0)
596 	{
597 		return;
598 	}
599 
600 	const QUrl url(m_page->history()->currentItem().url());
601 	const QVariant state(m_page->history()->currentItem().userData());
602 
603 	if (state.isValid())
604 	{
605 		const quint64 identifier(state.toList().value(IdentifierEntryData).toULongLong());
606 
607 		if (identifier > 0)
608 		{
609 			HistoryManager::updateEntry(identifier, url, getTitle(), m_page->mainFrame()->icon());
610 		}
611 	}
612 	else
613 	{
614 		m_page->history()->currentItem().setUserData(QVariantList({(Utils::isUrlEmpty(url) ? 0 : HistoryManager::addEntry(url, getTitle(), m_page->mainFrame()->icon(), m_isTyped)), getZoom(), QPoint(0, 0), QDateTime::currentDateTimeUtc()}));
615 
616 		if (m_isTyped)
617 		{
618 			m_isTyped = false;
619 		}
620 
621 		SessionsManager::markSessionAsModified();
622 		BookmarksManager::updateVisits(url.toString());
623 	}
624 }
625 
handleNavigationRequest(const QUrl & url,QWebPage::NavigationType type)626 void QtWebKitWebWidget::handleNavigationRequest(const QUrl &url, QWebPage::NavigationType type)
627 {
628 	if (type != QWebPage::NavigationTypeBackOrForward && (type != QWebPage::NavigationTypeLinkClicked || !getUrl().matches(url, QUrl::RemoveFragment)))
629 	{
630 		handleLoadStarted();
631 		handleHistory();
632 
633 		m_networkManager->resetStatistics();
634 	}
635 
636 	updateOptions(url);
637 
638 	m_isNavigating = true;
639 
640 	emit aboutToNavigate();
641 }
642 
handleFullScreenRequest(QWebFullScreenRequest request)643 void QtWebKitWebWidget::handleFullScreenRequest(QWebFullScreenRequest request)
644 {
645 	request.accept();
646 
647 	if (request.toggleOn())
648 	{
649 		const QString value(SettingsManager::getOption(SettingsManager::Permissions_EnableFullScreenOption, Utils::extractHost(request.origin())).toString());
650 
651 		if (value == QLatin1String("allow"))
652 		{
653 			MainWindow *mainWindow(MainWindow::findMainWindow(this));
654 
655 			if (mainWindow && !mainWindow->isFullScreen())
656 			{
657 				mainWindow->triggerAction(ActionsManager::FullScreenAction);
658 			}
659 		}
660 		else if (value == QLatin1String("ask"))
661 		{
662 			emit requestedPermission(FullScreenFeature, request.origin(), false);
663 		}
664 	}
665 	else
666 	{
667 		MainWindow *mainWindow(MainWindow::findMainWindow(this));
668 
669 		if (mainWindow && mainWindow->isFullScreen())
670 		{
671 			mainWindow->triggerAction(ActionsManager::FullScreenAction);
672 		}
673 
674 		emit requestedPermission(FullScreenFeature, request.origin(), true);
675 	}
676 
677 	m_isFullScreen = request.toggleOn();
678 
679 	emit isFullScreenChanged(m_isFullScreen);
680 }
681 
handlePermissionRequest(QWebFrame * frame,QWebPage::Feature feature)682 void QtWebKitWebWidget::handlePermissionRequest(QWebFrame *frame, QWebPage::Feature feature)
683 {
684 	notifyPermissionRequested(frame, feature, false);
685 }
686 
handlePermissionCancel(QWebFrame * frame,QWebPage::Feature feature)687 void QtWebKitWebWidget::handlePermissionCancel(QWebFrame *frame, QWebPage::Feature feature)
688 {
689 	notifyPermissionRequested(frame, feature, true);
690 }
691 
notifyTitleChanged()692 void QtWebKitWebWidget::notifyTitleChanged()
693 {
694 	emit titleChanged(getTitle());
695 
696 	handleHistory();
697 }
698 
notifyUrlChanged(const QUrl & url)699 void QtWebKitWebWidget::notifyUrlChanged(const QUrl &url)
700 {
701 	m_isNavigating = false;
702 
703 	updateOptions(url);
704 
705 	emit urlChanged(url);
706 	emit arbitraryActionsStateChanged({ActionsManager::InspectPageAction, ActionsManager::InspectElementAction});
707 	emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::NavigationCategory, ActionsManager::ActionDefinition::PageCategory});
708 
709 	SessionsManager::markSessionAsModified();
710 }
711 
notifyIconChanged()712 void QtWebKitWebWidget::notifyIconChanged()
713 {
714 	emit iconChanged(getIcon());
715 }
716 
notifyPermissionRequested(QWebFrame * frame,QWebPage::Feature nativeFeature,bool cancel)717 void QtWebKitWebWidget::notifyPermissionRequested(QWebFrame *frame, QWebPage::Feature nativeFeature, bool cancel)
718 {
719 	FeaturePermission feature(UnknownFeature);
720 
721 	switch (nativeFeature)
722 	{
723 		case QWebPage::Geolocation:
724 			feature = GeolocationFeature;
725 
726 			break;
727 		case QWebPage::Notifications:
728 			feature = NotificationsFeature;
729 
730 			break;
731 		default:
732 			return;
733 	}
734 
735 	const QUrl url(frame->url().isValid() ? frame->url() : frame->requestedUrl());
736 
737 	if (cancel)
738 	{
739 		emit requestedPermission(feature, url, true);
740 	}
741 	else
742 	{
743 		switch (getPermission(feature, url))
744 		{
745 			case GrantedPermission:
746 				m_page->setFeaturePermission(frame, nativeFeature, QWebPage::PermissionGrantedByUser);
747 
748 				break;
749 			case DeniedPermission:
750 				m_page->setFeaturePermission(frame, nativeFeature, QWebPage::PermissionDeniedByUser);
751 
752 				break;
753 			default:
754 				emit requestedPermission(feature, url, false);
755 
756 				break;
757 		}
758 	}
759 }
760 
notifySavePasswordRequested(const PasswordsManager::PasswordInformation & password,bool isUpdate)761 void QtWebKitWebWidget::notifySavePasswordRequested(const PasswordsManager::PasswordInformation &password, bool isUpdate)
762 {
763 	emit requestedSavePassword(password, isUpdate);
764 }
765 
updateAmountOfDeferredPlugins()766 void QtWebKitWebWidget::updateAmountOfDeferredPlugins()
767 {
768 	const int amountOfDeferredPlugins(m_canLoadPlugins ? 0 : findChildren<QtWebKitPluginWidget*>().count());
769 
770 	if (amountOfDeferredPlugins != m_amountOfDeferredPlugins)
771 	{
772 		const bool needsActionUpdate(amountOfDeferredPlugins == 0 || m_amountOfDeferredPlugins == 0);
773 
774 		m_amountOfDeferredPlugins = amountOfDeferredPlugins;
775 
776 		if (needsActionUpdate)
777 		{
778 			emit arbitraryActionsStateChanged({ActionsManager::LoadPluginsAction});
779 		}
780 	}
781 }
782 
updateOptions(const QUrl & url)783 void QtWebKitWebWidget::updateOptions(const QUrl &url)
784 {
785 	const QString encoding(getOption(SettingsManager::Content_DefaultCharacterEncodingOption, url).toString());
786 	const bool arePluginsEnabled(getOption(SettingsManager::Permissions_EnablePluginsOption, url).toString() != QLatin1String("disabled"));
787 	QWebSettings *settings(m_page->settings());
788 	settings->setAttribute(QWebSettings::AutoLoadImages, (getOption(SettingsManager::Permissions_EnableImagesOption, url).toString() != QLatin1String("onlyCached")));
789 	settings->setAttribute(QWebSettings::PluginsEnabled, arePluginsEnabled);
790 	settings->setAttribute(QWebSettings::JavaEnabled, arePluginsEnabled);
791 	settings->setAttribute(QWebSettings::JavascriptEnabled, (m_page->isDisplayingErrorPage() || m_page->isViewingMedia() || getOption(SettingsManager::Permissions_EnableJavaScriptOption, url).toBool()));
792 	settings->setAttribute(QWebSettings::JavascriptCanAccessClipboard, getOption(SettingsManager::Permissions_ScriptsCanAccessClipboardOption, url).toBool());
793 	settings->setAttribute(QWebSettings::JavascriptCanCloseWindows, getOption(SettingsManager::Permissions_ScriptsCanCloseWindowsOption, url).toBool());
794 	settings->setAttribute(QWebSettings::JavascriptCanOpenWindows, (getOption(SettingsManager::Permissions_ScriptsCanOpenWindowsOption, url).toString() != QLatin1String("blockAll")));
795 	settings->setAttribute(QWebSettings::WebGLEnabled, getOption(SettingsManager::Permissions_EnableWebglOption, url).toBool());
796 	settings->setAttribute(QWebSettings::LocalStorageEnabled, getOption(SettingsManager::Permissions_EnableLocalStorageOption, url).toBool());
797 	settings->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, getOption(SettingsManager::Permissions_EnableOfflineStorageDatabaseOption, url).toBool());
798 	settings->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, getOption(SettingsManager::Permissions_EnableOfflineWebApplicationCacheOption, url).toBool());
799 	settings->setAttribute(QWebSettings::AllowRunningInsecureContent, getOption(SettingsManager::Security_AllowMixedContentOption, url).toBool());
800 	settings->setAttribute(QWebSettings::MediaEnabled, getOption(QtWebKitWebBackend::getOptionIdentifier(QtWebKitWebBackend::QtWebKitBackend_EnableMediaOption), url).toBool());
801 	settings->setAttribute(QWebSettings::MediaSourceEnabled, getOption(QtWebKitWebBackend::getOptionIdentifier(QtWebKitWebBackend::QtWebKitBackend_EnableMediaSourceOption), url).toBool());
802 	settings->setAttribute(QWebSettings::WebSecurityEnabled, getOption(QtWebKitWebBackend::getOptionIdentifier(QtWebKitWebBackend::QtWebKitBackend_EnableWebSecurityOption), url).toBool());
803 	settings->setDefaultTextEncoding((encoding == QLatin1String("auto")) ? QString() : encoding);
804 
805 	disconnect(m_page, &QtWebKitPage::geometryChangeRequested, this, &QtWebKitWebWidget::requestedGeometryChange);
806 	disconnect(m_page, &QtWebKitPage::statusBarMessage, this, &QtWebKitWebWidget::setStatusMessage);
807 
808 	if (getOption(SettingsManager::Permissions_ScriptsCanChangeWindowGeometryOption, url).toBool())
809 	{
810 		connect(m_page, &QtWebKitPage::geometryChangeRequested, this, &QtWebKitWebWidget::requestedGeometryChange);
811 	}
812 
813 	if (getOption(SettingsManager::Permissions_ScriptsCanShowStatusMessagesOption, url).toBool())
814 	{
815 		connect(m_page, &QtWebKitPage::statusBarMessage, this, &QtWebKitWebWidget::setStatusMessage);
816 	}
817 	else
818 	{
819 		setStatusMessage({});
820 	}
821 
822 	m_page->updateStyleSheets(url);
823 
824 	m_networkManager->updateOptions(url);
825 
826 	m_canLoadPlugins = (getOption(SettingsManager::Permissions_EnablePluginsOption, url).toString() == QLatin1String("enabled"));
827 }
828 
clearOptions()829 void QtWebKitWebWidget::clearOptions()
830 {
831 	WebWidget::clearOptions();
832 
833 	updateOptions(getUrl());
834 }
835 
fillPassword(const PasswordsManager::PasswordInformation & password)836 void QtWebKitWebWidget::fillPassword(const PasswordsManager::PasswordInformation &password)
837 {
838 	QFile file(QLatin1String(":/modules/backends/web/qtwebkit/resources/formFiller.js"));
839 
840 	if (!file.open(QIODevice::ReadOnly))
841 	{
842 		return;
843 	}
844 
845 	QJsonArray fieldsArray;
846 
847 	for (int i = 0; i < password.fields.count(); ++i)
848 	{
849 		fieldsArray.append(QJsonObject({{QLatin1String("name"), password.fields.at(i).name}, {QLatin1String("value"), password.fields.at(i).value}, {QLatin1String("type"), ((password.fields.at(i).type == PasswordsManager::PasswordField) ? QLatin1String("password") : QLatin1String("text"))}}));
850 	}
851 
852 	const QString script(QString(file.readAll()).arg(QString(QJsonDocument(fieldsArray).toJson(QJsonDocument::Indented))));
853 
854 	file.close();
855 
856 	QList<QWebFrame*> frames({m_page->mainFrame()});
857 
858 	while (!frames.isEmpty())
859 	{
860 		const QWebFrame *frame(frames.takeFirst());
861 		frame->documentElement().evaluateJavaScript(script);
862 
863 		frames.append(frame->childFrames());
864 	}
865 }
866 
triggerAction(int identifier,const QVariantMap & parameters,ActionsManager::TriggerType trigger)867 void QtWebKitWebWidget::triggerAction(int identifier, const QVariantMap &parameters, ActionsManager::TriggerType trigger)
868 {
869 	switch (identifier)
870 	{
871 		case ActionsManager::SaveAction:
872 			if (m_page->isViewingMedia())
873 			{
874 				const SaveInformation information(Utils::getSavePath(suggestSaveFileName(SingleFileSaveFormat)));
875 
876 				if (information.canSave)
877 				{
878 					QNetworkRequest request(getUrl());
879 					request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
880 
881 					new Transfer(m_networkManager->get(request), information.path, (Transfer::CanAskForPathOption | Transfer::CanAutoDeleteOption | Transfer::CanOverwriteOption | Transfer::IsPrivateOption));
882 				}
883 			}
884 			else
885 			{
886 				SaveFormat format(UnknownSaveFormat);
887 				const QString path(getSavePath({SingleFileSaveFormat, PdfSaveFormat}, &format));
888 
889 				if (!path.isEmpty())
890 				{
891 					switch (format)
892 					{
893 						case PdfSaveFormat:
894 							{
895 								QPrinter printer;
896 								printer.setOutputFormat(QPrinter::PdfFormat);
897 								printer.setOutputFileName(path);
898 								printer.setCreator(QStringLiteral("Otter Browser %1").arg(Application::getFullVersion()));
899 								printer.setDocName(getTitle());
900 
901 								m_page->mainFrame()->print(&printer);
902 							}
903 
904 							break;
905 						default:
906 							{
907 								QNetworkRequest request(getUrl());
908 								request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
909 
910 								new Transfer(m_networkManager->get(request), path, (Transfer::CanAskForPathOption | Transfer::CanAutoDeleteOption | Transfer::CanOverwriteOption | Transfer::IsPrivateOption));
911 							}
912 
913 							break;
914 					}
915 				}
916 			}
917 
918 			break;
919 		case ActionsManager::ClearTabHistoryAction:
920 			if (parameters.value(QLatin1String("clearGlobalHistory")).toBool())
921 			{
922 				for (int i = 0; i < m_page->history()->count(); ++i)
923 				{
924 					const quint64 historyIdentifier(m_page->history()->itemAt(i).userData().toList().value(IdentifierEntryData).toULongLong());
925 
926 					if (historyIdentifier > 0)
927 					{
928 						HistoryManager::removeEntry(historyIdentifier);
929 					}
930 				}
931 			}
932 
933 			setUrl(QUrl(QLatin1String("about:blank")));
934 
935 			m_page->history()->clear();
936 
937 			emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::NavigationCategory});
938 
939 			break;
940 		case ActionsManager::PurgeTabHistoryAction:
941 			triggerAction(ActionsManager::ClearTabHistoryAction, {{QLatin1String("clearGlobalHistory"), true}}, trigger);
942 
943 			break;
944 		case ActionsManager::MuteTabMediaAction:
945 			m_isAudioMuted = !m_isAudioMuted;
946 
947 			muteAudio(m_page->mainFrame(), m_isAudioMuted);
948 
949 			emit arbitraryActionsStateChanged({ActionsManager::MuteTabMediaAction});
950 
951 			break;
952 		case ActionsManager::OpenLinkAction:
953 			{
954 				const SessionsManager::OpenHints hints(SessionsManager::calculateOpenHints(parameters));
955 
956 				if (hints == SessionsManager::DefaultOpen && !getCurrentHitTestResult().flags.testFlag(HitTestResult::IsLinkFromSelectionTest))
957 				{
958 					m_page->triggerAction(QWebPage::OpenLink);
959 
960 					setClickPosition({});
961 				}
962 				else if (getCurrentHitTestResult().linkUrl.isValid())
963 				{
964 					openUrl(getCurrentHitTestResult().linkUrl, hints);
965 				}
966 			}
967 
968 			break;
969 		case ActionsManager::OpenLinkInCurrentTabAction:
970 			if (getCurrentHitTestResult().linkUrl.isValid())
971 			{
972 				openUrl(getCurrentHitTestResult().linkUrl, SessionsManager::CurrentTabOpen);
973 			}
974 
975 			break;
976 		case ActionsManager::OpenLinkInNewTabAction:
977 			if (getCurrentHitTestResult().linkUrl.isValid())
978 			{
979 				openUrl(getCurrentHitTestResult().linkUrl, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen));
980 			}
981 
982 			break;
983 		case ActionsManager::OpenLinkInNewTabBackgroundAction:
984 			if (getCurrentHitTestResult().linkUrl.isValid())
985 			{
986 				openUrl(getCurrentHitTestResult().linkUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen));
987 			}
988 
989 			break;
990 		case ActionsManager::OpenLinkInNewWindowAction:
991 			if (getCurrentHitTestResult().linkUrl.isValid())
992 			{
993 				openUrl(getCurrentHitTestResult().linkUrl, SessionsManager::calculateOpenHints(SessionsManager::NewWindowOpen));
994 			}
995 
996 			break;
997 		case ActionsManager::OpenLinkInNewWindowBackgroundAction:
998 			if (getCurrentHitTestResult().linkUrl.isValid())
999 			{
1000 				openUrl(getCurrentHitTestResult().linkUrl, (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen));
1001 			}
1002 
1003 			break;
1004 		case ActionsManager::OpenLinkInNewPrivateTabAction:
1005 			if (getCurrentHitTestResult().linkUrl.isValid())
1006 			{
1007 				openUrl(getCurrentHitTestResult().linkUrl, (SessionsManager::NewTabOpen | SessionsManager::PrivateOpen));
1008 			}
1009 
1010 			break;
1011 		case ActionsManager::OpenLinkInNewPrivateTabBackgroundAction:
1012 			if (getCurrentHitTestResult().linkUrl.isValid())
1013 			{
1014 				openUrl(getCurrentHitTestResult().linkUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen));
1015 			}
1016 
1017 			break;
1018 		case ActionsManager::OpenLinkInNewPrivateWindowAction:
1019 			if (getCurrentHitTestResult().linkUrl.isValid())
1020 			{
1021 				openUrl(getCurrentHitTestResult().linkUrl, (SessionsManager::NewWindowOpen | SessionsManager::PrivateOpen));
1022 			}
1023 
1024 			break;
1025 		case ActionsManager::OpenLinkInNewPrivateWindowBackgroundAction:
1026 			if (getCurrentHitTestResult().linkUrl.isValid())
1027 			{
1028 				openUrl(getCurrentHitTestResult().linkUrl, (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen));
1029 			}
1030 
1031 			break;
1032 		case ActionsManager::CopyLinkToClipboardAction:
1033 			if (!getCurrentHitTestResult().linkUrl.isEmpty())
1034 			{
1035 				Application::clipboard()->setText(getCurrentHitTestResult().linkUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
1036 			}
1037 
1038 			break;
1039 		case ActionsManager::BookmarkLinkAction:
1040 			if (getCurrentHitTestResult().linkUrl.isValid())
1041 			{
1042 				const QString title(getCurrentHitTestResult().title);
1043 
1044 				Application::triggerAction(ActionsManager::BookmarkPageAction, {{QLatin1String("url"), getCurrentHitTestResult().linkUrl}, {QLatin1String("title"), (title.isEmpty() ? m_page->mainFrame()->hitTestContent(getCurrentHitTestResult().hitPosition).element().toPlainText() : title)}}, parentWidget(), trigger);
1045 			}
1046 
1047 			break;
1048 		case ActionsManager::SaveLinkToDiskAction:
1049 			if (getCurrentHitTestResult().linkUrl.isValid())
1050 			{
1051 				handleDownloadRequested(QNetworkRequest(getCurrentHitTestResult().linkUrl));
1052 			}
1053 			else
1054 			{
1055 				m_page->triggerAction(QWebPage::DownloadLinkToDisk);
1056 			}
1057 
1058 			break;
1059 		case ActionsManager::SaveLinkToDownloadsAction:
1060 			TransfersManager::addTransfer(new Transfer(getCurrentHitTestResult().linkUrl.toString(), {}, (Transfer::CanNotifyOption | Transfer::CanAskForPathOption | Transfer::IsQuickTransferOption | (isPrivate() ? Transfer::IsPrivateOption : Transfer::NoOption))));
1061 
1062 			break;
1063 		case ActionsManager::OpenFrameAction:
1064 			if (getCurrentHitTestResult().frameUrl.isValid())
1065 			{
1066 				openUrl(getCurrentHitTestResult().frameUrl, SessionsManager::calculateOpenHints(parameters));
1067 			}
1068 
1069 			break;
1070 		case ActionsManager::OpenFrameInCurrentTabAction:
1071 			if (getCurrentHitTestResult().frameUrl.isValid())
1072 			{
1073 				setUrl(getCurrentHitTestResult().frameUrl, false);
1074 			}
1075 
1076 			break;
1077 		case ActionsManager::OpenFrameInNewTabAction:
1078 			if (getCurrentHitTestResult().frameUrl.isValid())
1079 			{
1080 				openUrl(getCurrentHitTestResult().frameUrl, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen));
1081 			}
1082 
1083 			break;
1084 		case ActionsManager::OpenFrameInNewTabBackgroundAction:
1085 			if (getCurrentHitTestResult().frameUrl.isValid())
1086 			{
1087 				openUrl(getCurrentHitTestResult().frameUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen));
1088 			}
1089 
1090 			break;
1091 		case ActionsManager::CopyFrameLinkToClipboardAction:
1092 			if (getCurrentHitTestResult().frameUrl.isValid())
1093 			{
1094 				Application::clipboard()->setText(getCurrentHitTestResult().frameUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
1095 			}
1096 
1097 			break;
1098 		case ActionsManager::ReloadFrameAction:
1099 			if (getCurrentHitTestResult().frameUrl.isValid())
1100 			{
1101 				const QUrl url(getCurrentHitTestResult().frameUrl);
1102 				QWebHitTestResult hitResult(m_page->mainFrame()->hitTestContent(getCurrentHitTestResult().hitPosition));
1103 
1104 				if (hitResult.frame())
1105 				{
1106 					m_networkManager->addContentBlockingException(url, NetworkManager::SubFrameType);
1107 
1108 					hitResult.frame()->setUrl({});
1109 					hitResult.frame()->setUrl(url);
1110 				}
1111 			}
1112 
1113 			break;
1114 		case ActionsManager::ViewFrameSourceAction:
1115 			if (getCurrentHitTestResult().frameUrl.isValid())
1116 			{
1117 				const QString defaultEncoding(m_page->settings()->defaultTextEncoding());
1118 				QNetworkRequest request(getCurrentHitTestResult().frameUrl);
1119 				request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
1120 
1121 				QNetworkReply *reply(m_networkManager->get(request));
1122 				SourceViewerWebWidget *sourceViewer(new SourceViewerWebWidget(isPrivate()));
1123 				sourceViewer->setRequestedUrl(QUrl(QLatin1String("view-source:") + getCurrentHitTestResult().frameUrl.toString()), false);
1124 
1125 				if (!defaultEncoding.isEmpty())
1126 				{
1127 					sourceViewer->setOption(SettingsManager::Content_DefaultCharacterEncodingOption, defaultEncoding);
1128 				}
1129 
1130 				m_viewSourceReplies[reply] = sourceViewer;
1131 
1132 				connect(reply, &QNetworkReply::finished, this, &QtWebKitWebWidget::handleViewSourceReplyFinished);
1133 
1134 				emit requestedNewWindow(sourceViewer, SessionsManager::DefaultOpen, {});
1135 			}
1136 
1137 			break;
1138 		case ActionsManager::OpenImageAction:
1139 			if (getCurrentHitTestResult().imageUrl.isValid())
1140 			{
1141 				openUrl(getCurrentHitTestResult().imageUrl, SessionsManager::calculateOpenHints(parameters));
1142 			}
1143 
1144 			break;
1145 		case ActionsManager::OpenImageInNewTabAction:
1146 			if (getCurrentHitTestResult().imageUrl.isValid())
1147 			{
1148 				openUrl(getCurrentHitTestResult().imageUrl, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen));
1149 			}
1150 
1151 			break;
1152 		case ActionsManager::OpenImageInNewTabBackgroundAction:
1153 			if (getCurrentHitTestResult().imageUrl.isValid())
1154 			{
1155 				openUrl(getCurrentHitTestResult().imageUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen));
1156 			}
1157 
1158 			break;
1159 		case ActionsManager::SaveImageToDiskAction:
1160 			if (getCurrentHitTestResult().imageUrl.isValid())
1161 			{
1162 				handleDownloadRequested(QNetworkRequest(getCurrentHitTestResult().imageUrl));
1163 			}
1164 
1165 			break;
1166 		case ActionsManager::CopyImageToClipboardAction:
1167 			{
1168 				const QPixmap pixmap(m_page->mainFrame()->hitTestContent(getCurrentHitTestResult().hitPosition).pixmap());
1169 
1170 				if (!pixmap.isNull())
1171 				{
1172 					Application::clipboard()->setPixmap(pixmap);
1173 				}
1174 				else
1175 				{
1176 					m_page->triggerAction(QWebPage::CopyImageToClipboard);
1177 				}
1178 			}
1179 
1180 			break;
1181 		case ActionsManager::CopyImageUrlToClipboardAction:
1182 			if (!getCurrentHitTestResult().imageUrl.isEmpty())
1183 			{
1184 				Application::clipboard()->setText(getCurrentHitTestResult().imageUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
1185 			}
1186 
1187 			break;
1188 		case ActionsManager::ReloadImageAction:
1189 			if (!getCurrentHitTestResult().imageUrl.isEmpty())
1190 			{
1191 				m_networkManager->addContentBlockingException(getCurrentHitTestResult().imageUrl, NetworkManager::ImageType);
1192 
1193 				m_page->settings()->setAttribute(QWebSettings::AutoLoadImages, true);
1194 
1195 				if (getUrl().matches(getCurrentHitTestResult().imageUrl, (QUrl::NormalizePathSegments | QUrl::RemoveFragment | QUrl::StripTrailingSlash)))
1196 				{
1197 					triggerAction(ActionsManager::ReloadAndBypassCacheAction, {}, trigger);
1198 				}
1199 				else
1200 				{
1201 					const QWebHitTestResult hitResult(m_page->mainFrame()->hitTestContent(getCurrentHitTestResult().hitPosition));
1202 					const QUrl url(getCurrentHitTestResult().imageUrl);
1203 					const QString src(hitResult.element().attribute(QLatin1String("src")));
1204 					NetworkCache *cache(NetworkManagerFactory::getCache());
1205 
1206 					hitResult.element().setAttribute(QLatin1String("src"), {});
1207 
1208 					if (cache)
1209 					{
1210 						cache->remove(url);
1211 					}
1212 
1213 					hitResult.element().setAttribute(QLatin1String("src"), src);
1214 
1215 					m_page->mainFrame()->documentElement().evaluateJavaScript(QStringLiteral("var images = document.querySelectorAll('img[src=\"%1\"]'); for (var i = 0; i < images.length; ++i) { images[i].src = ''; images[i].src = '%1'; }").arg(src));
1216 				}
1217 			}
1218 
1219 			break;
1220 		case ActionsManager::ImagePropertiesAction:
1221 			{
1222 				QVariantMap properties({{QLatin1String("alternativeText"), getCurrentHitTestResult().alternateText}, {QLatin1String("longDescription"), getCurrentHitTestResult().longDescription}});
1223 				const QWebHitTestResult hitResult(m_page->mainFrame()->hitTestContent(getCurrentHitTestResult().hitPosition));
1224 
1225 				if (!hitResult.pixmap().isNull())
1226 				{
1227 					properties[QLatin1String("width")] = hitResult.pixmap().width();
1228 					properties[QLatin1String("height")] = hitResult.pixmap().height();
1229 					properties[QLatin1String("depth")] = hitResult.pixmap().depth();
1230 				}
1231 
1232 				ImagePropertiesDialog *imagePropertiesDialog(new ImagePropertiesDialog(getCurrentHitTestResult().imageUrl, properties, (m_networkManager->cache() ? m_networkManager->cache()->data(getCurrentHitTestResult().imageUrl) : nullptr), this));
1233 				imagePropertiesDialog->setButtonsVisible(false);
1234 
1235 				ContentsDialog *dialog(new ContentsDialog(ThemesManager::createIcon(QLatin1String("dialog-information")), imagePropertiesDialog->windowTitle(), {}, {}, QDialogButtonBox::Close, imagePropertiesDialog, this));
1236 
1237 				connect(this, &QtWebKitWebWidget::aboutToReload, dialog, &ContentsDialog::close);
1238 				connect(imagePropertiesDialog, &ImagePropertiesDialog::finished, dialog, &ContentsDialog::close);
1239 
1240 				showDialog(dialog, false);
1241 			}
1242 
1243 			break;
1244 		case ActionsManager::SaveMediaToDiskAction:
1245 			if (getCurrentHitTestResult().mediaUrl.isValid())
1246 			{
1247 				handleDownloadRequested(QNetworkRequest(getCurrentHitTestResult().mediaUrl));
1248 			}
1249 
1250 			break;
1251 		case ActionsManager::CopyMediaUrlToClipboardAction:
1252 			if (!getCurrentHitTestResult().mediaUrl.isEmpty())
1253 			{
1254 				Application::clipboard()->setText(getCurrentHitTestResult().mediaUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
1255 			}
1256 
1257 			break;
1258 		case ActionsManager::MediaControlsAction:
1259 			m_page->triggerAction(QWebPage::ToggleMediaControls, parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool());
1260 
1261 			break;
1262 		case ActionsManager::MediaLoopAction:
1263 			m_page->triggerAction(QWebPage::ToggleMediaLoop, parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool());
1264 
1265 			break;
1266 		case ActionsManager::MediaPlayPauseAction:
1267 			m_page->triggerAction(QWebPage::ToggleMediaPlayPause);
1268 
1269 			break;
1270 		case ActionsManager::MediaMuteAction:
1271 			m_page->triggerAction(QWebPage::ToggleMediaMute);
1272 
1273 			break;
1274 		case ActionsManager::MediaPlaybackRateAction:
1275 			m_page->mainFrame()->hitTestContent(getCurrentHitTestResult().hitPosition).element().evaluateJavaScript(QStringLiteral("this.playbackRate = %1").arg(parameters.value(QLatin1String("rate"), 1).toReal()));
1276 
1277 			break;
1278 		case ActionsManager::GoBackAction:
1279 			m_page->triggerAction(QWebPage::Back);
1280 
1281 			break;
1282 		case ActionsManager::GoForwardAction:
1283 			m_page->triggerAction(QWebPage::Forward);
1284 
1285 			break;
1286 		case ActionsManager::GoToHistoryIndexAction:
1287 			if (parameters.contains(QLatin1String("index")))
1288 			{
1289 				const int index(parameters[QLatin1String("index")].toInt());
1290 
1291 				if (index >= 0 && index < m_page->history()->count())
1292 				{
1293 					m_page->history()->goToItem(m_page->history()->itemAt(index));
1294 				}
1295 			}
1296 
1297 			break;
1298 		case ActionsManager::RewindAction:
1299 			m_page->history()->goToItem(m_page->history()->itemAt(0));
1300 
1301 			break;
1302 		case ActionsManager::FastForwardAction:
1303 			{
1304 				const QUrl url(m_page->mainFrame()->evaluateJavaScript(getFastForwardScript(true)).toUrl());
1305 
1306 				if (url.isValid())
1307 				{
1308 					setUrl(url);
1309 				}
1310 				else if (canGoForward())
1311 				{
1312 					m_page->triggerAction(QWebPage::Forward);
1313 				}
1314 			}
1315 
1316 			break;
1317 		case ActionsManager::RemoveHistoryIndexAction:
1318 			if (parameters.contains(QLatin1String("index")))
1319 			{
1320 				const int index(parameters[QLatin1String("index")].toInt());
1321 
1322 				if (index >= 0 && index < m_page->history()->count())
1323 				{
1324 					if (parameters.value(QLatin1String("clearGlobalHistory"), false).toBool())
1325 					{
1326 						const quint64 entryIdentifier(m_page->history()->itemAt(index).userData().toList().value(IdentifierEntryData).toULongLong());
1327 
1328 						if (entryIdentifier > 0)
1329 						{
1330 							HistoryManager::removeEntry(entryIdentifier);
1331 						}
1332 					}
1333 
1334 					WindowHistoryInformation history(getHistory());
1335 					history.entries.removeAt(index);
1336 
1337 					if (history.index >= index)
1338 					{
1339 						history.index = (history.index - 1);
1340 					}
1341 
1342 					setHistory(history);
1343 				}
1344 			}
1345 
1346 			break;
1347 		case ActionsManager::StopAction:
1348 			m_page->triggerAction(QWebPage::Stop);
1349 
1350 			break;
1351 		case ActionsManager::StopScheduledReloadAction:
1352 			m_page->triggerAction(QWebPage::StopScheduledPageRefresh);
1353 
1354 			break;
1355 		case ActionsManager::ReloadAction:
1356 			emit aboutToReload();
1357 
1358 			m_page->triggerAction(QWebPage::Stop);
1359 			m_page->triggerAction(QWebPage::Reload);
1360 
1361 			break;
1362 		case ActionsManager::ReloadOrStopAction:
1363 			if (m_loadingState == OngoingLoadingState)
1364 			{
1365 				triggerAction(ActionsManager::StopAction, {}, trigger);
1366 			}
1367 			else
1368 			{
1369 				triggerAction(ActionsManager::ReloadAction, {}, trigger);
1370 			}
1371 
1372 			break;
1373 		case ActionsManager::ReloadAndBypassCacheAction:
1374 			m_page->triggerAction(QWebPage::ReloadAndBypassCache);
1375 
1376 			break;
1377 		case ActionsManager::ContextMenuAction:
1378 			if (parameters.contains(QLatin1String("context")) && parameters[QLatin1String("context")].toInt() == QContextMenuEvent::Keyboard)
1379 			{
1380 				const QWebElement element(m_page->mainFrame()->findFirstElement(QLatin1String(":focus")));
1381 
1382 				if (element.isNull())
1383 				{
1384 					setClickPosition(m_webView->mapFromGlobal(QCursor::pos()));
1385 				}
1386 				else
1387 				{
1388 					QPoint clickPosition(element.geometry().center());
1389 					QWebFrame *frame(element.webFrame());
1390 
1391 					while (frame)
1392 					{
1393 						clickPosition -= frame->scrollPosition();
1394 
1395 						frame = frame->parentFrame();
1396 					}
1397 
1398 					setClickPosition(clickPosition);
1399 				}
1400 			}
1401 			else
1402 			{
1403 				setClickPosition(m_webView->mapFromGlobal(QCursor::pos()));
1404 			}
1405 
1406 			showContextMenu(getClickPosition());
1407 
1408 			break;
1409 		case ActionsManager::UndoAction:
1410 			m_page->triggerAction(QWebPage::Undo);
1411 
1412 			break;
1413 		case ActionsManager::RedoAction:
1414 			m_page->triggerAction(QWebPage::Redo);
1415 
1416 			break;
1417 		case ActionsManager::CutAction:
1418 			m_page->triggerAction(QWebPage::Cut);
1419 
1420 			break;
1421 		case ActionsManager::CopyAction:
1422 			if (parameters.value(QLatin1String("mode")) == QLatin1String("plainText"))
1423 			{
1424 				const QString text(getSelectedText());
1425 
1426 				if (!text.isEmpty())
1427 				{
1428 					Application::clipboard()->setText(text);
1429 				}
1430 			}
1431 			else
1432 			{
1433 				m_page->triggerAction(QWebPage::Copy);
1434 			}
1435 
1436 			break;
1437 		case ActionsManager::CopyPlainTextAction:
1438 			triggerAction(ActionsManager::CopyAction, {{QLatin1String("mode"), QLatin1String("plainText")}}, trigger);
1439 
1440 			break;
1441 		case ActionsManager::CopyAddressAction:
1442 			Application::clipboard()->setText(getUrl().toString());
1443 
1444 			break;
1445 		case ActionsManager::CopyToNoteAction:
1446 			NotesManager::addNote(BookmarksModel::UrlBookmark, {{BookmarksModel::UrlRole, getUrl()}, {BookmarksModel::DescriptionRole, getSelectedText()}});
1447 
1448 			break;
1449 		case ActionsManager::PasteAction:
1450 			if (parameters.contains(QLatin1String("note")))
1451 			{
1452 				const BookmarksModel::Bookmark *bookmark(NotesManager::getModel()->getBookmark(parameters[QLatin1String("note")].toULongLong()));
1453 
1454 				if (bookmark)
1455 				{
1456 					triggerAction(ActionsManager::PasteAction, {{QLatin1String("text"), bookmark->getDescription()}}, trigger);
1457 				}
1458 			}
1459 			else if (parameters.contains(QLatin1String("text")))
1460 			{
1461 				QMimeData *mimeData(new QMimeData());
1462 				const QStringList mimeTypes(Application::clipboard()->mimeData()->formats());
1463 
1464 				for (int i = 0; i < mimeTypes.count(); ++i)
1465 				{
1466 					mimeData->setData(mimeTypes.at(i), Application::clipboard()->mimeData()->data(mimeTypes.at(i)));
1467 				}
1468 
1469 				Application::clipboard()->setText(parameters[QLatin1String("text")].toString());
1470 
1471 				m_page->triggerAction(QWebPage::Paste);
1472 
1473 				Application::clipboard()->setMimeData(mimeData);
1474 			}
1475 			else
1476 			{
1477 				m_page->triggerAction(QWebPage::Paste);
1478 			}
1479 
1480 			break;
1481 		case ActionsManager::DeleteAction:
1482 			m_page->triggerAction(QWebPage::DeleteEndOfWord);
1483 
1484 			break;
1485 		case ActionsManager::SelectAllAction:
1486 			m_page->triggerAction(QWebPage::SelectAll);
1487 
1488 			break;
1489 		case ActionsManager::UnselectAction:
1490 			m_page->triggerAction(QWebPage::Unselect);
1491 
1492 			break;
1493 		case ActionsManager::ClearAllAction:
1494 			m_page->triggerAction(QWebPage::SelectAll);
1495 			m_page->triggerAction(QWebPage::DeleteEndOfWord);
1496 
1497 			break;
1498 		case ActionsManager::CheckSpellingAction:
1499 			{
1500 				QWebElement element(m_page->mainFrame()->hitTestContent(getCurrentHitTestResult().hitPosition).element());
1501 				element.evaluateJavaScript(QStringLiteral("this.spellcheck = %1").arg(parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool() ? QLatin1String("true") : QLatin1String("false")));
1502 
1503 				if (parameters.contains(QLatin1String("dictionary")))
1504 				{
1505 					setOption(SettingsManager::Browser_SpellCheckDictionaryOption, parameters.value(QLatin1String("dictionary")));
1506 				}
1507 				else
1508 				{
1509 					resetSpellCheck(element);
1510 				}
1511 
1512 				emit arbitraryActionsStateChanged({ActionsManager::CheckSpellingAction});
1513 			}
1514 
1515 			break;
1516 		case ActionsManager::CreateSearchAction:
1517 			{
1518 				const QWebHitTestResult hitResult(m_page->mainFrame()->hitTestContent(getCurrentHitTestResult().hitPosition));
1519 				QWebElement parentElement(hitResult.element().parent());
1520 
1521 				while (!parentElement.isNull() && parentElement.tagName().toLower() != QLatin1String("form"))
1522 				{
1523 					parentElement = parentElement.parent();
1524 				}
1525 
1526 				if (!parentElement.hasAttribute(QLatin1String("action")))
1527 				{
1528 					break;
1529 				}
1530 
1531 				QList<QWebElement> inputElements(parentElement.findAll(QLatin1String("button:not([disabled])[name][type='submit'], input:not([disabled])[name], select:not([disabled])[name], textarea:not([disabled])[name]")).toList());
1532 
1533 				if (inputElements.isEmpty())
1534 				{
1535 					break;
1536 				}
1537 
1538 				QWebElement searchTermsElement;
1539 				const QString tagName(hitResult.element().tagName().toLower());
1540 				const QUrl url(parentElement.attribute(QLatin1String("action")));
1541 				const QIcon icon(m_page->mainFrame()->icon());
1542 				SearchEnginesManager::SearchEngineDefinition searchEngine;
1543 				searchEngine.title = getTitle();
1544 				searchEngine.formUrl = getUrl();
1545 				searchEngine.icon = (icon.isNull() ? ThemesManager::createIcon(QLatin1String("edit-find")) : icon);
1546 				searchEngine.resultsUrl.url = (url.isEmpty() ? getUrl() : resolveUrl(parentElement.webFrame(), url)).toString();
1547 				searchEngine.resultsUrl.enctype = parentElement.attribute(QLatin1String("enctype"));
1548 				searchEngine.resultsUrl.method = ((parentElement.attribute(QLatin1String("method"), QLatin1String("get")).toLower() == QLatin1String("post")) ? QLatin1String("post") : QLatin1String("get"));
1549 
1550 				if (tagName != QLatin1String("select"))
1551 				{
1552 					const QString type(hitResult.element().attribute(QLatin1String("type")));
1553 
1554 					if (!inputElements.contains(hitResult.element()) && (type == QLatin1String("image") || type == QLatin1String("submit")))
1555 					{
1556 						inputElements.append(hitResult.element());
1557 					}
1558 
1559 					if (tagName == QLatin1String("textarea") || type == QLatin1String("email") || type == QLatin1String("password") || type == QLatin1String("search") || type == QLatin1String("tel") || type == QLatin1String("text") || type == QLatin1String("url"))
1560 					{
1561 						searchTermsElement = hitResult.element();
1562 					}
1563 				}
1564 
1565 				if (searchTermsElement.isNull())
1566 				{
1567 					searchTermsElement = parentElement.findFirst(QLatin1String("input:not([disabled])[name][type='search']"));
1568 				}
1569 
1570 				for (int i = 0; i < inputElements.count(); ++i)
1571 				{
1572 					const QString name(inputElements.at(i).attribute(QLatin1String("name")));
1573 
1574 					if (inputElements.at(i).tagName().toLower() != QLatin1String("select"))
1575 					{
1576 						const QString type(inputElements.at(i).attribute(QLatin1String("type")));
1577 						const bool isSubmit(type == QLatin1String("image") || type == QLatin1String("submit"));
1578 
1579 						if ((isSubmit && inputElements.at(i) != hitResult.element()) || ((type == QLatin1String("checkbox") || type == QLatin1String("radio")) && !inputElements[i].evaluateJavaScript(QLatin1String("this.checked")).toBool()))
1580 						{
1581 							continue;
1582 						}
1583 
1584 						if (isSubmit && inputElements.at(i) == hitResult.element())
1585 						{
1586 							if (inputElements.at(i).hasAttribute(QLatin1String("formaction")))
1587 							{
1588 								searchEngine.resultsUrl.url = resolveUrl(parentElement.webFrame(), inputElements.at(i).attribute(QLatin1String("formaction"))).toString();
1589 							}
1590 
1591 							if (inputElements.at(i).hasAttribute(QLatin1String("formenctype")))
1592 							{
1593 								searchEngine.resultsUrl.enctype = inputElements.at(i).attribute(QLatin1String("formenctype"));
1594 							}
1595 
1596 							if (inputElements.at(i).hasAttribute(QLatin1String("formmethod")))
1597 							{
1598 								searchEngine.resultsUrl.method = inputElements.at(i).attribute(QLatin1String("formmethod"));
1599 							}
1600 						}
1601 
1602 						if (!isSubmit && searchTermsElement.isNull() && type != QLatin1String("hidden"))
1603 						{
1604 							searchTermsElement = inputElements.at(i);
1605 						}
1606 
1607 						if (!name.isEmpty())
1608 						{
1609 							searchEngine.resultsUrl.parameters.addQueryItem(name, ((inputElements.at(i) == searchTermsElement) ? QLatin1String("{searchTerms}") : inputElements[i].evaluateJavaScript((inputElements.at(i).tagName().toLower() == QLatin1String("button")) ? QLatin1String("this.innerHTML") : QLatin1String("this.value")).toString()));
1610 						}
1611 					}
1612 					else if (!name.isEmpty())
1613 					{
1614 						const QWebElementCollection optionElements(inputElements.at(i).findAll(QLatin1String("option:checked")));
1615 
1616 						for (int j = 0; j < optionElements.count(); ++j)
1617 						{
1618 							searchEngine.resultsUrl.parameters.addQueryItem(name, optionElements.at(j).evaluateJavaScript(QLatin1String("this.value")).toString());
1619 						}
1620 					}
1621 				}
1622 
1623 				SearchEnginePropertiesDialog dialog(searchEngine, SearchEnginesManager::getSearchKeywords(), this);
1624 
1625 				if (dialog.exec() == QDialog::Accepted)
1626 				{
1627 					SearchEnginesManager::addSearchEngine(dialog.getSearchEngine());
1628 				}
1629 			}
1630 
1631 			break;
1632 		case ActionsManager::ScrollToStartAction:
1633 			m_page->mainFrame()->setScrollPosition(QPoint(m_page->mainFrame()->scrollPosition().x(), 0));
1634 
1635 			break;
1636 		case ActionsManager::ScrollToEndAction:
1637 			m_page->mainFrame()->setScrollPosition(QPoint(m_page->mainFrame()->scrollPosition().x(), m_page->mainFrame()->scrollBarMaximum(Qt::Vertical)));
1638 
1639 			break;
1640 		case ActionsManager::ScrollPageUpAction:
1641 			m_page->mainFrame()->setScrollPosition(QPoint(m_page->mainFrame()->scrollPosition().x(), qMax(0, (m_page->mainFrame()->scrollPosition().y() - m_webView->height()))));
1642 
1643 			break;
1644 		case ActionsManager::ScrollPageDownAction:
1645 			m_page->mainFrame()->setScrollPosition(QPoint(m_page->mainFrame()->scrollPosition().x(), qMin(m_page->mainFrame()->scrollBarMaximum(Qt::Vertical), (m_page->mainFrame()->scrollPosition().y() + m_webView->height()))));
1646 
1647 			break;
1648 		case ActionsManager::ScrollPageLeftAction:
1649 			m_page->mainFrame()->setScrollPosition(QPoint(qMax(0, (m_page->mainFrame()->scrollPosition().x() - m_webView->width())), m_page->mainFrame()->scrollPosition().y()));
1650 
1651 			break;
1652 		case ActionsManager::ScrollPageRightAction:
1653 			m_page->mainFrame()->setScrollPosition(QPoint(qMin(m_page->mainFrame()->scrollBarMaximum(Qt::Horizontal), (m_page->mainFrame()->scrollPosition().x() + m_webView->width())), m_page->mainFrame()->scrollPosition().y()));
1654 
1655 			break;
1656 		case ActionsManager::TakeScreenshotAction:
1657 			{
1658 				const QString mode(parameters.value(QLatin1String("mode"), QLatin1String("viewport")).toString());
1659 				const QSize viewportSize(m_page->viewportSize());
1660 				const QSize contentsSize((mode == QLatin1String("viewport")) ? viewportSize : m_page->mainFrame()->contentsSize());
1661 				const QRect rectangle((mode == QLatin1String("area")) ? JsonSettings::readRectangle(parameters.value(QLatin1String("geometry"))) : QRect());
1662 				QPixmap pixmap(rectangle.isValid() ? rectangle.size() : contentsSize);
1663 				QPainter painter(&pixmap);
1664 
1665 				m_page->setViewportSize(contentsSize);
1666 
1667 				if (rectangle.isValid())
1668 				{
1669 					painter.translate(-rectangle.topLeft());
1670 
1671 					m_page->mainFrame()->render(&painter, {rectangle});
1672 				}
1673 				else
1674 				{
1675 					m_page->mainFrame()->render(&painter);
1676 				}
1677 
1678 				m_page->setViewportSize(viewportSize);
1679 
1680 				const QStringList filters({tr("PNG image (*.png)"), tr("JPEG image (*.jpg *.jpeg)")});
1681 				const SaveInformation result(Utils::getSavePath(suggestSaveFileName(QLatin1String(".png")), {}, filters));
1682 
1683 				if (result.canSave)
1684 				{
1685 					pixmap.save(result.path, ((filters.indexOf(result.filter) == 0) ? "PNG" : "JPEG"));
1686 				}
1687 			}
1688 
1689 			break;
1690 		case ActionsManager::ActivateContentAction:
1691 			{
1692 				m_webView->setFocus();
1693 
1694 				m_page->mainFrame()->setFocus();
1695 
1696 				const QString tagName(m_page->mainFrame()->findFirstElement(QLatin1String(":focus")).tagName().toLower());
1697 
1698 				if (tagName == QLatin1String("textarea") || tagName == QLatin1String("input"))
1699 				{
1700 					m_page->mainFrame()->documentElement().evaluateJavaScript(QLatin1String("document.activeElement.blur()"));
1701 				}
1702 			}
1703 
1704 			break;
1705 		case ActionsManager::LoadPluginsAction:
1706 			{
1707 				m_canLoadPlugins = true;
1708 
1709 				QList<QWebFrame*> frames({m_page->mainFrame()});
1710 
1711 				while (!frames.isEmpty())
1712 				{
1713 					const QWebFrame *frame(frames.takeFirst());
1714 					const QWebElementCollection elements(frame->documentElement().findAll(QLatin1String("object, embed")));
1715 
1716 					for (int i = 0; i < elements.count(); ++i)
1717 					{
1718 						elements.at(i).replace(elements.at(i).clone());
1719 					}
1720 
1721 					frames.append(frame->childFrames());
1722 				}
1723 
1724 				m_amountOfDeferredPlugins = 0;
1725 
1726 				emit arbitraryActionsStateChanged({ActionsManager::LoadPluginsAction});
1727 			}
1728 
1729 			break;
1730 		case ActionsManager::ViewSourceAction:
1731 			if (canViewSource())
1732 			{
1733 				const QString defaultEncoding(m_page->settings()->defaultTextEncoding());
1734 				QNetworkRequest request(getUrl());
1735 				request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
1736 
1737 				QNetworkReply *reply(m_networkManager->get(request));
1738 				SourceViewerWebWidget *sourceViewer(new SourceViewerWebWidget(isPrivate()));
1739 				sourceViewer->setRequestedUrl(QUrl(QLatin1String("view-source:") + getUrl().toString()), false);
1740 
1741 				if (!defaultEncoding.isEmpty())
1742 				{
1743 					sourceViewer->setOption(SettingsManager::Content_DefaultCharacterEncodingOption, defaultEncoding);
1744 				}
1745 
1746 				m_viewSourceReplies[reply] = sourceViewer;
1747 
1748 				connect(reply, &QNetworkReply::finished, this, &QtWebKitWebWidget::handleViewSourceReplyFinished);
1749 
1750 				emit requestedNewWindow(sourceViewer, SessionsManager::DefaultOpen, {});
1751 			}
1752 
1753 			break;
1754 		case ActionsManager::InspectPageAction:
1755 			{
1756 				const bool showInspector(parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool());
1757 
1758 				if (showInspector && !m_inspector)
1759 				{
1760 					getInspector();
1761 				}
1762 
1763 				emit requestedInspectorVisibilityChange(showInspector);
1764 				emit arbitraryActionsStateChanged({ActionsManager::InspectPageAction});
1765 			}
1766 
1767 			break;
1768 		case ActionsManager::InspectElementAction:
1769 			triggerAction(ActionsManager::InspectPageAction, {{QLatin1String("isChecked"), true}}, trigger);
1770 
1771 			m_page->triggerAction(QWebPage::InspectElement);
1772 
1773 			break;
1774 		case ActionsManager::FullScreenAction:
1775 			{
1776 				const MainWindow *mainWindow(MainWindow::findMainWindow(this));
1777 
1778 				if (mainWindow && !mainWindow->isFullScreen())
1779 				{
1780 					m_page->mainFrame()->evaluateJavaScript(QLatin1String("document.webkitExitFullscreen()"));
1781 				}
1782 			}
1783 
1784 			break;
1785 		case ActionsManager::WebsitePreferencesAction:
1786 			{
1787 				const QUrl url(getUrl());
1788 				CookieJar *cookieJar(m_networkManager->getCookieJar());
1789 				WebsitePreferencesDialog dialog(Utils::extractHost(url), (url.host().isEmpty() ? QVector<QNetworkCookie>() : cookieJar->getCookies(url.host())), this);
1790 
1791 				if (dialog.exec() == QDialog::Accepted)
1792 				{
1793 					updateOptions(url);
1794 
1795 					const QVector<QNetworkCookie> cookiesToDelete(dialog.getCookiesToDelete());
1796 					const QVector<QNetworkCookie> cookiesToInsert(dialog.getCookiesToInsert());
1797 
1798 					for (int i = 0; i < cookiesToDelete.count(); ++i)
1799 					{
1800 						cookieJar->forceDeleteCookie(cookiesToDelete.at(i));
1801 					}
1802 
1803 					for (int i = 0; i < cookiesToInsert.count(); ++i)
1804 					{
1805 						cookieJar->forceInsertCookie(cookiesToInsert.at(i));
1806 					}
1807 				}
1808 			}
1809 
1810 			break;
1811 		default:
1812 			break;
1813 	}
1814 }
1815 
setActiveStyleSheet(const QString & styleSheet)1816 void QtWebKitWebWidget::setActiveStyleSheet(const QString &styleSheet)
1817 {
1818 	const QWebElementCollection elements(m_page->mainFrame()->findAllElements(QLatin1String("link[rel='alternate stylesheet']")));
1819 
1820 	for (int i = 0; i < elements.count(); ++i)
1821 	{
1822 		elements.at(i).evaluateJavaScript(QLatin1String("this.disabled = ") + ((elements.at(i).attribute(QLatin1String("title")) == styleSheet) ? QLatin1String("false") : QLatin1String("true")));
1823 	}
1824 }
1825 
setHistory(const WindowHistoryInformation & history)1826 void QtWebKitWebWidget::setHistory(const WindowHistoryInformation &history)
1827 {
1828 	if (history.entries.isEmpty())
1829 	{
1830 		m_page->history()->clear();
1831 
1832 		updateOptions({});
1833 
1834 		if (m_page->getMainFrame())
1835 		{
1836 			m_page->getMainFrame()->runUserScripts(QUrl(QLatin1String("about:blank")));
1837 		}
1838 
1839 		emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::NavigationCategory, ActionsManager::ActionDefinition::PageCategory});
1840 
1841 		return;
1842 	}
1843 
1844 	const int index(qMin(history.index, (m_page->history()->maximumItemCount() - 1)));
1845 	QVariantList entries;
1846 	entries.reserve(history.entries.count());
1847 
1848 	for (int i = 0; i < history.entries.count(); ++i)
1849 	{
1850 		QVariantMap entry;
1851 		entry[QLatin1String("pageScaleFactor")] = 0;
1852 		entry[QLatin1String("title")] = history.entries.at(i).title;
1853 		entry[QLatin1String("urlString")] = QString(QUrl::fromUserInput(history.entries.at(i).url).toEncoded());
1854 		entry[QLatin1String("scrollPosition")] = QVariantMap({{QLatin1String("x"), history.entries.at(i).position.x()}, {QLatin1String("y"), history.entries.at(i).position.y()}});
1855 
1856 		entries.append(entry);
1857 	}
1858 
1859 	m_page->history()->loadFromMap({{QLatin1String("currentItemIndex"), index}, {QLatin1String("history"), entries}});
1860 
1861 	for (int i = 0; i < history.entries.count(); ++i)
1862 	{
1863 		m_page->history()->itemAt(i).setUserData(QVariantList({-1, history.entries.at(i).zoom, history.entries.at(i).position, history.entries.at(i).time}));
1864 	}
1865 
1866 	m_page->history()->goToItem(m_page->history()->itemAt(index));
1867 
1868 	const QUrl url(m_page->history()->itemAt(index).url());
1869 
1870 	setRequestedUrl(url, false, true);
1871 	updateOptions(url);
1872 
1873 	m_page->triggerAction(QWebPage::Reload);
1874 
1875 	emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::PageCategory});
1876 }
1877 
setHistory(const QVariantMap & history)1878 void QtWebKitWebWidget::setHistory(const QVariantMap &history)
1879 {
1880 	m_page->history()->loadFromMap(history);
1881 
1882 	const QUrl url(m_page->history()->currentItem().url());
1883 
1884 	setRequestedUrl(url, false, true);
1885 	updateOptions(url);
1886 
1887 	m_page->triggerAction(QWebPage::Reload);
1888 
1889 	emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::PageCategory});
1890 }
1891 
setZoom(int zoom)1892 void QtWebKitWebWidget::setZoom(int zoom)
1893 {
1894 	if (zoom != getZoom())
1895 	{
1896 		m_page->mainFrame()->setZoomFactor(qBound(0.1, (static_cast<qreal>(zoom) / 100), static_cast<qreal>(100)));
1897 
1898 		SessionsManager::markSessionAsModified();
1899 
1900 		emit zoomChanged(zoom);
1901 		emit geometryChanged();
1902 	}
1903 }
1904 
setUrl(const QUrl & url,bool isTyped)1905 void QtWebKitWebWidget::setUrl(const QUrl &url, bool isTyped)
1906 {
1907 	if (url.scheme() == QLatin1String("javascript"))
1908 	{
1909 		m_page->mainFrame()->documentElement().evaluateJavaScript(url.toDisplayString(QUrl::RemoveScheme | QUrl::DecodeReserved));
1910 	}
1911 	else if (!url.fragment().isEmpty() && url.matches(getUrl(), (QUrl::RemoveFragment | QUrl::StripTrailingSlash | QUrl::NormalizePathSegments)))
1912 	{
1913 		m_page->mainFrame()->scrollToAnchor(url.fragment());
1914 	}
1915 	else
1916 	{
1917 		m_isTyped = isTyped;
1918 
1919 		const QUrl targetUrl(Utils::expandUrl(url));
1920 
1921 		updateOptions(targetUrl);
1922 
1923 		m_page->mainFrame()->load(targetUrl);
1924 
1925 		notifyTitleChanged();
1926 		notifyIconChanged();
1927 	}
1928 }
1929 
setPermission(FeaturePermission feature,const QUrl & url,PermissionPolicies policies)1930 void QtWebKitWebWidget::setPermission(FeaturePermission feature, const QUrl &url, PermissionPolicies policies)
1931 {
1932 	WebWidget::setPermission(feature, url, policies);
1933 
1934 	if (feature == FullScreenFeature)
1935 	{
1936 		if (policies.testFlag(GrantedPermission))
1937 		{
1938 			MainWindow *mainWindow(MainWindow::findMainWindow(this));
1939 
1940 			if (mainWindow && !mainWindow->isFullScreen())
1941 			{
1942 				mainWindow->triggerAction(ActionsManager::FullScreenAction);
1943 			}
1944 		}
1945 
1946 		return;
1947 	}
1948 
1949 	QWebPage::Feature nativeFeature(QWebPage::Geolocation);
1950 
1951 	switch (feature)
1952 	{
1953 		case GeolocationFeature:
1954 			nativeFeature = QWebPage::Geolocation;
1955 
1956 			break;
1957 		case NotificationsFeature:
1958 			nativeFeature = QWebPage::Notifications;
1959 
1960 			break;
1961 		default:
1962 			return;
1963 	}
1964 
1965 	QList<QWebFrame*> frames({m_page->mainFrame()});
1966 
1967 	while (!frames.isEmpty())
1968 	{
1969 		QWebFrame *frame(frames.takeFirst());
1970 
1971 		if (frame->requestedUrl() == url)
1972 		{
1973 			m_page->setFeaturePermission(frame, nativeFeature, (policies.testFlag(GrantedPermission) ? QWebPage::PermissionGrantedByUser : QWebPage::PermissionDeniedByUser));
1974 		}
1975 
1976 		frames.append(frame->childFrames());
1977 	}
1978 }
1979 
setOption(int identifier,const QVariant & value)1980 void QtWebKitWebWidget::setOption(int identifier, const QVariant &value)
1981 {
1982 	WebWidget::setOption(identifier, value);
1983 
1984 	updateOptions(getUrl());
1985 
1986 	switch (identifier)
1987 	{
1988 		case SettingsManager::Content_DefaultCharacterEncodingOption:
1989 			m_page->triggerAction(QWebPage::Reload);
1990 
1991 			break;
1992 		case SettingsManager::Browser_SpellCheckDictionaryOption:
1993 			emit widgetActivated(this);
1994 
1995 			resetSpellCheck(m_page->mainFrame()->hitTestContent(getCurrentHitTestResult().hitPosition).element());
1996 
1997 			break;
1998 		default:
1999 			break;
2000 	}
2001 }
2002 
setOptions(const QHash<int,QVariant> & options,const QStringList & excludedOptions)2003 void QtWebKitWebWidget::setOptions(const QHash<int, QVariant> &options, const QStringList &excludedOptions)
2004 {
2005 	WebWidget::setOptions(options, excludedOptions);
2006 
2007 	updateOptions(getUrl());
2008 }
2009 
setScrollPosition(const QPoint & position)2010 void QtWebKitWebWidget::setScrollPosition(const QPoint &position)
2011 {
2012 	m_page->mainFrame()->setScrollPosition(position);
2013 }
2014 
clone(bool cloneHistory,bool isPrivate,const QStringList & excludedOptions) const2015 WebWidget* QtWebKitWebWidget::clone(bool cloneHistory, bool isPrivate, const QStringList &excludedOptions) const
2016 {
2017 	QtWebKitNetworkManager *networkManager((this->isPrivate() != isPrivate) ? nullptr : m_networkManager->clone());
2018 	QtWebKitWebWidget *widget((this->isPrivate() || isPrivate) ? new QtWebKitWebWidget({{QLatin1String("hints"), SessionsManager::PrivateOpen}}, getBackend(), networkManager, nullptr) : new QtWebKitWebWidget({}, getBackend(), networkManager, nullptr));
2019 	widget->getPage()->setViewportSize(m_page->viewportSize());
2020 	widget->setOptions(getOptions(), excludedOptions);
2021 
2022 	if (cloneHistory)
2023 	{
2024 		widget->setHistory(m_page->history()->toMap());
2025 	}
2026 
2027 	widget->setZoom(getZoom());
2028 
2029 	return widget;
2030 }
2031 
getInspector()2032 QWidget* QtWebKitWebWidget::getInspector()
2033 {
2034 	if (!m_inspector)
2035 	{
2036 		m_page->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
2037 
2038 		m_inspector = new QtWebKitInspector(this);
2039 		m_inspector->setPage(m_page);
2040 	}
2041 
2042 	return m_inspector;
2043 }
2044 
getViewport()2045 QWidget* QtWebKitWebWidget::getViewport()
2046 {
2047 	return m_webView;
2048 }
2049 
getPage() const2050 QtWebKitPage* QtWebKitWebWidget::getPage() const
2051 {
2052 	return m_page;
2053 }
2054 
getTitle() const2055 QString QtWebKitWebWidget::getTitle() const
2056 {
2057 	const QString title(m_page->mainFrame()->title());
2058 
2059 	if (!title.isEmpty())
2060 	{
2061 		return title;
2062 	}
2063 
2064 	const QUrl url(getUrl());
2065 
2066 	if (Utils::isUrlEmpty(url))
2067 	{
2068 		return tr("Blank Page");
2069 	}
2070 
2071 	if (url.isLocalFile())
2072 	{
2073 		return QFileInfo(url.toLocalFile()).canonicalFilePath();
2074 	}
2075 
2076 	if (!url.isEmpty())
2077 	{
2078 		return url.toString();
2079 	}
2080 
2081 	return tr("(Untitled)");
2082 }
2083 
getDescription() const2084 QString QtWebKitWebWidget::getDescription() const
2085 {
2086 	const QString description(m_page->mainFrame()->findFirstElement(QLatin1String("[name='description']")).attribute(QLatin1String("content")));
2087 
2088 	return (description.isEmpty() ? m_page->mainFrame()->findFirstElement(QLatin1String("[name='og:description']")).attribute(QLatin1String("property")) : description);
2089 }
2090 
getActiveStyleSheet() const2091 QString QtWebKitWebWidget::getActiveStyleSheet() const
2092 {
2093 	if (m_page->mainFrame()->documentElement().evaluateJavaScript(QLatin1String("var isDefault = true; for (var i = 0; i < document.styleSheets.length; ++i) { if (document.styleSheets[i].ownerNode.rel.indexOf('alt') >= 0) { isDefault = false; break; } } isDefault")).toBool())
2094 	{
2095 		return {};
2096 	}
2097 
2098 	return m_page->mainFrame()->findFirstElement(QLatin1String("link[rel='alternate stylesheet']:not([disabled])")).attribute(QLatin1String("title"));
2099 }
2100 
getSelectedText() const2101 QString QtWebKitWebWidget::getSelectedText() const
2102 {
2103 	return m_page->selectedText();
2104 }
2105 
getMessageToken() const2106 QString QtWebKitWebWidget::getMessageToken() const
2107 {
2108 	return m_messageToken;
2109 }
2110 
getPluginToken() const2111 QString QtWebKitWebWidget::getPluginToken() const
2112 {
2113 	return m_pluginToken;
2114 }
2115 
getPageInformation(PageInformation key) const2116 QVariant QtWebKitWebWidget::getPageInformation(PageInformation key) const
2117 {
2118 	if (key == LoadingTimeInformation)
2119 	{
2120 		return WebWidget::getPageInformation(LoadingTimeInformation);
2121 	}
2122 
2123 	return m_networkManager->getPageInformation(key);
2124 }
2125 
resolveUrl(QWebFrame * frame,const QUrl & url) const2126 QUrl QtWebKitWebWidget::resolveUrl(QWebFrame *frame, const QUrl &url) const
2127 {
2128 	if (url.isRelative())
2129 	{
2130 		return (frame ? frame->baseUrl() : getUrl()).resolved(url);
2131 	}
2132 
2133 	return url;
2134 }
2135 
getUrl() const2136 QUrl QtWebKitWebWidget::getUrl() const
2137 {
2138 	const QUrl url(m_page->mainFrame()->url());
2139 
2140 	return (Utils::isUrlEmpty(url) ? m_page->mainFrame()->requestedUrl() : url);
2141 }
2142 
getIcon() const2143 QIcon QtWebKitWebWidget::getIcon() const
2144 {
2145 	if (isPrivate())
2146 	{
2147 		return ThemesManager::createIcon(QLatin1String("tab-private"));
2148 	}
2149 
2150 	const QIcon icon(m_page->mainFrame()->icon());
2151 
2152 	return (icon.isNull() ? ThemesManager::createIcon(QLatin1String("tab")) : icon);
2153 }
2154 
createThumbnail(const QSize & size)2155 QPixmap QtWebKitWebWidget::createThumbnail(const QSize &size)
2156 {
2157 	if ((size.isNull() || size == m_thumbnail.size()) && ((!m_thumbnail.isNull() && qFuzzyCompare(m_thumbnail.devicePixelRatio(), devicePixelRatio())) || m_loadingState == OngoingLoadingState))
2158 	{
2159 		return m_thumbnail;
2160 	}
2161 
2162 	const QSize thumbnailSize(size.isValid() ? size : QSize(260, 170));
2163 	const QSize oldViewportSize(m_page->viewportSize());
2164 	const QPoint position(m_page->mainFrame()->scrollPosition());
2165 	const qreal zoom(m_page->mainFrame()->zoomFactor());
2166 	QSize contentsSize(m_page->mainFrame()->contentsSize());
2167 	QWidget *newView(new QWidget());
2168 	QWidget *oldView(m_page->view());
2169 
2170 	m_page->setView(newView);
2171 	m_page->setViewportSize(contentsSize);
2172 	m_page->mainFrame()->setZoomFactor(1);
2173 
2174 	if (contentsSize.width() > 2000)
2175 	{
2176 		contentsSize.setWidth(2000);
2177 	}
2178 
2179 	contentsSize.setHeight(qRound(thumbnailSize.height() * (static_cast<qreal>(contentsSize.width()) / thumbnailSize.width())));
2180 
2181 	m_page->setViewportSize(contentsSize);
2182 
2183 	QPixmap pixmap(contentsSize);
2184 	pixmap.fill(Qt::white);
2185 
2186 	QPainter painter(&pixmap);
2187 
2188 	m_page->mainFrame()->render(&painter, QWebFrame::ContentsLayer, QRegion(QRect(QPoint(0, 0), contentsSize)));
2189 	m_page->mainFrame()->setZoomFactor(zoom);
2190 	m_page->setView(oldView);
2191 	m_page->setViewportSize(oldViewportSize);
2192 	m_page->mainFrame()->setScrollPosition(position);
2193 
2194 	painter.end();
2195 
2196 	pixmap = pixmap.scaled((thumbnailSize * devicePixelRatio()), Qt::KeepAspectRatio, Qt::SmoothTransformation);
2197 	pixmap.setDevicePixelRatio(devicePixelRatio());
2198 
2199 	newView->deleteLater();
2200 
2201 	if (size.isNull())
2202 	{
2203 		m_thumbnail = pixmap;
2204 	}
2205 
2206 	return pixmap;
2207 }
2208 
getScrollPosition() const2209 QPoint QtWebKitWebWidget::getScrollPosition() const
2210 {
2211 	return m_page->mainFrame()->scrollPosition();
2212 }
2213 
getGeometry(bool excludeScrollBars) const2214 QRect QtWebKitWebWidget::getGeometry(bool excludeScrollBars) const
2215 {
2216 	if (!excludeScrollBars)
2217 	{
2218 		return geometry();
2219 	}
2220 
2221 	QRect geometry(this->geometry());
2222 	const QRect horizontalScrollBar(m_page->mainFrame()->scrollBarGeometry(Qt::Horizontal));
2223 	const QRect verticalScrollBar(m_page->mainFrame()->scrollBarGeometry(Qt::Vertical));
2224 
2225 	if (horizontalScrollBar.height() > 0 && geometry.intersects(horizontalScrollBar))
2226 	{
2227 		geometry.moveTop(m_webView->height() - horizontalScrollBar.height());
2228 	}
2229 
2230 	if (verticalScrollBar.width() > 0 && geometry.intersects(verticalScrollBar))
2231 	{
2232 		if (verticalScrollBar.left() == 0)
2233 		{
2234 			geometry.setLeft(verticalScrollBar.right());
2235 		}
2236 		else
2237 		{
2238 			geometry.setRight(verticalScrollBar.left());
2239 		}
2240 	}
2241 
2242 	return geometry;
2243 }
2244 
getActiveFrame() const2245 WebWidget::LinkUrl QtWebKitWebWidget::getActiveFrame() const
2246 {
2247 	const QWebFrame *frame(m_page->frameAt(getClickPosition()));
2248 	LinkUrl link;
2249 
2250 	if (frame && frame != m_page->mainFrame())
2251 	{
2252 		link.title = frame->title();
2253 		link.url = (frame->url().isValid() ? frame->url() : frame->requestedUrl());
2254 	}
2255 
2256 	return link;
2257 }
2258 
getActiveImage() const2259 WebWidget::LinkUrl QtWebKitWebWidget::getActiveImage() const
2260 {
2261 	const QWebHitTestResult result(m_page->mainFrame()->hitTestContent(getClickPosition()));
2262 	LinkUrl link;
2263 
2264 	if (!result.imageUrl().isEmpty())
2265 	{
2266 		link.title = result.alternateText();
2267 		link.url = result.imageUrl();
2268 	}
2269 
2270 	return link;
2271 }
2272 
getActiveLink() const2273 WebWidget::LinkUrl QtWebKitWebWidget::getActiveLink() const
2274 {
2275 	const QWebHitTestResult result(m_page->mainFrame()->hitTestContent(getClickPosition()));
2276 	LinkUrl link;
2277 
2278 	if (!result.linkElement().isNull())
2279 	{
2280 		link.title = result.linkElement().toPlainText();
2281 		link.url = result.linkUrl();
2282 	}
2283 
2284 	return link;
2285 }
2286 
getActiveMedia() const2287 WebWidget::LinkUrl QtWebKitWebWidget::getActiveMedia() const
2288 {
2289 	const QWebHitTestResult result(m_page->mainFrame()->hitTestContent(getClickPosition()));
2290 	LinkUrl link;
2291 
2292 	if (!result.mediaUrl().isEmpty())
2293 	{
2294 		link.title = result.title();
2295 		link.url = result.mediaUrl();
2296 	}
2297 
2298 	return link;
2299 }
2300 
getSslInformation() const2301 WebWidget::SslInformation QtWebKitWebWidget::getSslInformation() const
2302 {
2303 	return m_networkManager->getSslInformation();
2304 }
2305 
getHistory() const2306 WindowHistoryInformation QtWebKitWebWidget::getHistory() const
2307 {
2308 	QVariantList state(m_page->history()->currentItem().userData().toList());
2309 
2310 	if (state.isEmpty() || state.count() < 4)
2311 	{
2312 		state = {0, getZoom(), m_page->mainFrame()->scrollPosition(), QDateTime::currentDateTimeUtc()};
2313 	}
2314 	else
2315 	{
2316 		state[ZoomEntryData] = getZoom();
2317 		state[PositionEntryData] = m_page->mainFrame()->scrollPosition();
2318 	}
2319 
2320 	m_page->history()->currentItem().setUserData(state);
2321 
2322 	const QWebHistory *history(m_page->history());
2323 	const QUrl requestedUrl(m_page->mainFrame()->requestedUrl());
2324 	const int historyCount(history->count());
2325 	WindowHistoryInformation information;
2326 	information.entries.reserve(historyCount);
2327 	information.index = history->currentItemIndex();
2328 
2329 	for (int i = 0; i < historyCount; ++i)
2330 	{
2331 		const QWebHistoryItem item(history->itemAt(i));
2332 		const QVariantList itemState(item.userData().toList());
2333 		WindowHistoryEntry entry;
2334 		entry.url = item.url().toString();
2335 		entry.title = item.title();
2336 		entry.time = itemState.value(VisitTimeEntryData).toDateTime();
2337 		entry.position = itemState.value(PositionEntryData, QPoint(0, 0)).toPoint();
2338 		entry.zoom = itemState.value(ZoomEntryData).toInt();
2339 
2340 		const quint64 identifier(itemState.value(IdentifierEntryData).toULongLong());
2341 
2342 		if (identifier > 0)
2343 		{
2344 			const HistoryModel::Entry *globalEntry(HistoryManager::getEntry(identifier));
2345 
2346 			if (globalEntry)
2347 			{
2348 				entry.icon = globalEntry->icon();
2349 			}
2350 		}
2351 
2352 		information.entries.append(entry);
2353 	}
2354 
2355 	if (m_loadingState == OngoingLoadingState && requestedUrl != history->itemAt(history->currentItemIndex()).url())
2356 	{
2357 		WindowHistoryEntry entry;
2358 		entry.url = requestedUrl.toString();
2359 		entry.title = getTitle();
2360 		entry.position = state.value(PositionEntryData, QPoint(0, 0)).toPoint();
2361 		entry.zoom = state.value(ZoomEntryData).toInt();
2362 
2363 		information.index = historyCount;
2364 		information.entries.append(entry);
2365 	}
2366 
2367 	return information;
2368 }
2369 
getHitTestResult(const QPoint & position)2370 WebWidget::HitTestResult QtWebKitWebWidget::getHitTestResult(const QPoint &position)
2371 {
2372 	const QWebFrame *frame(m_page->frameAt(position));
2373 
2374 	if (!frame)
2375 	{
2376 		return {};
2377 	}
2378 
2379 	const QWebHitTestResult nativeResult(m_page->mainFrame()->hitTestContent(position));
2380 	HitTestResult result;
2381 	result.title = nativeResult.title();
2382 	result.tagName = nativeResult.element().tagName().toLower();
2383 	result.alternateText = nativeResult.element().attribute(QLatin1String("alt"));
2384 	result.longDescription = nativeResult.element().attribute(QLatin1String("longDesc"));
2385 	result.frameUrl = ((nativeResult.frame() && nativeResult.frame() != m_page->mainFrame()) ? (nativeResult.frame()->url().isValid() ? nativeResult.frame()->url() : nativeResult.frame()->requestedUrl()) : QUrl());
2386 	result.imageUrl = nativeResult.imageUrl();
2387 	result.linkUrl = nativeResult.linkUrl();
2388 	result.mediaUrl = nativeResult.mediaUrl();
2389 	result.hitPosition = position;
2390 	result.elementGeometry = nativeResult.element().geometry();
2391 
2392 	const QString type(nativeResult.element().attribute(QLatin1String("type")).toLower());
2393 	const bool isSubmit((result.tagName == QLatin1String("button") || result.tagName == QLatin1String("input")) && (type == QLatin1String("image") || type == QLatin1String("submit")));
2394 
2395 	if (isSubmit || result.tagName == QLatin1String("button") || result.tagName == QLatin1String("input") || result.tagName == QLatin1String("select") || result.tagName == QLatin1String("textarea"))
2396 	{
2397 		QWebElement parentElement(nativeResult.element().parent());
2398 
2399 		while (!parentElement.isNull() && parentElement.tagName().toLower() != QLatin1String("form"))
2400 		{
2401 			parentElement = parentElement.parent();
2402 		}
2403 
2404 		if (!parentElement.isNull() && parentElement.hasAttribute(QLatin1String("action")))
2405 		{
2406 			if (isSubmit)
2407 			{
2408 				const QUrl url(nativeResult.element().hasAttribute(QLatin1String("formaction")) ? nativeResult.element().attribute(QLatin1String("formaction")) : parentElement.attribute(QLatin1String("action")));
2409 
2410 				result.formUrl = (url.isEmpty() ? getUrl() : resolveUrl(parentElement.webFrame(), url)).toString();
2411 			}
2412 
2413 			if (parentElement.findAll(QLatin1String("input:not([disabled])[name], select:not([disabled])[name], textarea:not([disabled])[name]")).count() > 0)
2414 			{
2415 				result.flags |= HitTestResult::IsFormTest;
2416 			}
2417 		}
2418 	}
2419 
2420 	if (result.tagName == QLatin1String("textarea") || (result.tagName == QLatin1String("input") && (type.isEmpty() || type == QLatin1String("text") || type == QLatin1String("password") || type == QLatin1String("search"))))
2421 	{
2422 		if (!nativeResult.element().hasAttribute(QLatin1String("disabled")) && !nativeResult.element().hasAttribute(QLatin1String("readonly")))
2423 		{
2424 			result.flags |= HitTestResult::IsContentEditableTest;
2425 		}
2426 
2427 		if (nativeResult.element().evaluateJavaScript(QLatin1String("this.value")).toString().isEmpty())
2428 		{
2429 			result.flags |= HitTestResult::IsEmptyTest;
2430 		}
2431 	}
2432 	else if (nativeResult.isContentEditable())
2433 	{
2434 		result.flags |= HitTestResult::IsContentEditableTest;
2435 	}
2436 
2437 	if (nativeResult.isContentSelected())
2438 	{
2439 		result.flags |= HitTestResult::IsSelectedTest;
2440 	}
2441 
2442 	if (nativeResult.element().evaluateJavaScript(QLatin1String("this.spellcheck")).toBool())
2443 	{
2444 		result.flags |= HitTestResult::IsSpellCheckEnabled;
2445 	}
2446 
2447 	if (result.mediaUrl.isValid())
2448 	{
2449 		if (nativeResult.element().evaluateJavaScript(QLatin1String("this.controls")).toBool())
2450 		{
2451 			result.flags |= HitTestResult::MediaHasControlsTest;
2452 		}
2453 
2454 		if (nativeResult.element().evaluateJavaScript(QLatin1String("this.looped")).toBool())
2455 		{
2456 			result.flags |= HitTestResult::MediaIsLoopedTest;
2457 		}
2458 
2459 		if (nativeResult.element().evaluateJavaScript(QLatin1String("this.muted")).toBool())
2460 		{
2461 			result.flags |= HitTestResult::MediaIsMutedTest;
2462 		}
2463 
2464 		if (nativeResult.element().evaluateJavaScript(QLatin1String("this.paused")).toBool())
2465 		{
2466 			result.flags |= HitTestResult::MediaIsPausedTest;
2467 		}
2468 
2469 		result.playbackRate = nativeResult.element().evaluateJavaScript(QLatin1String("this.playbackRate")).toReal();
2470 	}
2471 
2472 	if (result.flags.testFlag(HitTestResult::IsSelectedTest) && !result.linkUrl.isValid() && Utils::isUrl(m_page->selectedText()))
2473 	{
2474 		result.flags |= HitTestResult::IsLinkFromSelectionTest;
2475 		result.linkUrl = QUrl::fromUserInput(m_page->selectedText());
2476 	}
2477 
2478 	return result;
2479 }
2480 
getBlockedElements() const2481 QStringList QtWebKitWebWidget::getBlockedElements() const
2482 {
2483 	return m_networkManager->getBlockedElements();
2484 }
2485 
getStyleSheets() const2486 QStringList QtWebKitWebWidget::getStyleSheets() const
2487 {
2488 	const QWebElementCollection elements(m_page->mainFrame()->findAllElements(QLatin1String("link[rel='alternate stylesheet']")));
2489 	QStringList titles;
2490 	titles.reserve(elements.count());
2491 
2492 	for (int i = 0; i < elements.count(); ++i)
2493 	{
2494 		const QString title(elements.at(i).attribute(QLatin1String("title")));
2495 
2496 		if (!title.isEmpty() && !titles.contains(title))
2497 		{
2498 			titles.append(title);
2499 		}
2500 	}
2501 
2502 	return titles;
2503 }
2504 
getFeeds() const2505 QVector<WebWidget::LinkUrl> QtWebKitWebWidget::getFeeds() const
2506 {
2507 	return getLinks(QLatin1String("a[type='application/atom+xml'], a[type='application/rss+xml'], link[type='application/atom+xml'], link[type='application/rss+xml']"));
2508 }
2509 
getLinks(const QString & query) const2510 QVector<WebWidget::LinkUrl> QtWebKitWebWidget::getLinks(const QString &query) const
2511 {
2512 	const QWebElementCollection elements(m_page->mainFrame()->findAllElements(query));
2513 	QSet<QUrl> urls;
2514 	urls.reserve(elements.count());
2515 
2516 	QVector<LinkUrl> links;
2517 	links.reserve(elements.count());
2518 
2519 	for (int i = 0; i < elements.count(); ++i)
2520 	{
2521 		const QUrl url(resolveUrl(m_page->mainFrame(), QUrl(elements.at(i).attribute(QLatin1String("href")))));
2522 
2523 		if (urls.contains(url))
2524 		{
2525 			continue;
2526 		}
2527 
2528 		urls.insert(url);
2529 
2530 		LinkUrl link;
2531 		link.title = elements.at(i).attribute(QLatin1String("title"));
2532 		link.mimeType = elements.at(i).attribute(QLatin1String("type"));
2533 		link.url = url;
2534 
2535 		if (link.title.isEmpty())
2536 		{
2537 			link.title = elements.at(i).toPlainText().simplified();
2538 		}
2539 
2540 		if (link.title.isEmpty())
2541 		{
2542 			const QWebElement imageElement(elements.at(i).findFirst(QLatin1String("img[alt]:not([alt=''])")));
2543 
2544 			if (!imageElement.isNull())
2545 			{
2546 				link.title = imageElement.attribute(QLatin1String("alt"));
2547 			}
2548 		}
2549 
2550 		links.append(link);
2551 	}
2552 
2553 	links.squeeze();
2554 
2555 	return links;
2556 }
2557 
getLinks() const2558 QVector<WebWidget::LinkUrl> QtWebKitWebWidget::getLinks() const
2559 {
2560 	return getLinks(QLatin1String("a[href]"));
2561 }
2562 
getSearchEngines() const2563 QVector<WebWidget::LinkUrl> QtWebKitWebWidget::getSearchEngines() const
2564 {
2565 	return getLinks(QLatin1String("link[type='application/opensearchdescription+xml']"));
2566 }
2567 
getBlockedRequests() const2568 QVector<NetworkManager::ResourceInformation> QtWebKitWebWidget::getBlockedRequests() const
2569 {
2570 	return m_networkManager->getBlockedRequests();
2571 }
2572 
getHeaders() const2573 QMap<QByteArray, QByteArray> QtWebKitWebWidget::getHeaders() const
2574 {
2575 	return m_networkManager->getHeaders();
2576 }
2577 
getMetaData() const2578 QMultiMap<QString, QString> QtWebKitWebWidget::getMetaData() const
2579 {
2580 	return m_page->mainFrame()->metaData();
2581 }
2582 
getContentState() const2583 WebWidget::ContentStates QtWebKitWebWidget::getContentState() const
2584 {
2585 	const QUrl url(getUrl());
2586 
2587 	if (url.isEmpty() || url.scheme() == QLatin1String("about"))
2588 	{
2589 		return ApplicationContentState;
2590 	}
2591 
2592 	if (url.scheme() == QLatin1String("file"))
2593 	{
2594 		return LocalContentState;
2595 	}
2596 
2597 	ContentStates state(m_networkManager->getContentState() | RemoteContentState);
2598 
2599 	if (getOption(SettingsManager::Security_EnableFraudCheckingOption, url).toBool() && ContentFiltersManager::isFraud(url))
2600 	{
2601 		state |= FraudContentState;
2602 	}
2603 
2604 	return state;
2605 }
2606 
getLoadingState() const2607 WebWidget::LoadingState QtWebKitWebWidget::getLoadingState() const
2608 {
2609 	return m_loadingState;
2610 }
2611 
getZoom() const2612 int QtWebKitWebWidget::getZoom() const
2613 {
2614 	return static_cast<int>(m_page->mainFrame()->zoomFactor() * 100);
2615 }
2616 
getAmountOfDeferredPlugins() const2617 int QtWebKitWebWidget::getAmountOfDeferredPlugins() const
2618 {
2619 	return m_amountOfDeferredPlugins;
2620 }
2621 
findInPage(const QString & text,FindFlags flags)2622 int QtWebKitWebWidget::findInPage(const QString &text, FindFlags flags)
2623 {
2624 	QWebPage::FindFlags nativeFlags(QWebPage::FindWrapsAroundDocument | QWebPage::FindBeginsInSelection);
2625 
2626 	if (flags.testFlag(BackwardFind))
2627 	{
2628 		nativeFlags |= QWebPage::FindBackward;
2629 	}
2630 
2631 	if (flags.testFlag(CaseSensitiveFind))
2632 	{
2633 		nativeFlags |= QWebPage::FindCaseSensitively;
2634 	}
2635 
2636 	if (flags.testFlag(HighlightAllFind) || text.isEmpty())
2637 	{
2638 		m_page->findText({}, QWebPage::HighlightAllOccurrences);
2639 		m_page->findText(text, (nativeFlags | QWebPage::HighlightAllOccurrences));
2640 	}
2641 
2642 	return (m_page->findText(text, nativeFlags) ? -1 : 0);
2643 }
2644 
canLoadPlugins() const2645 bool QtWebKitWebWidget::canLoadPlugins() const
2646 {
2647 	return m_canLoadPlugins;
2648 }
2649 
canGoBack() const2650 bool QtWebKitWebWidget::canGoBack() const
2651 {
2652 	return m_page->history()->canGoBack();
2653 }
2654 
canGoForward() const2655 bool QtWebKitWebWidget::canGoForward() const
2656 {
2657 	return m_page->history()->canGoForward();
2658 }
2659 
canFastForward() const2660 bool QtWebKitWebWidget::canFastForward() const
2661 {
2662 	if (canGoForward())
2663 	{
2664 		return true;
2665 	}
2666 
2667 	if (Utils::isUrlEmpty(getUrl()))
2668 	{
2669 		return false;
2670 	}
2671 
2672 	return m_page->mainFrame()->evaluateJavaScript(getFastForwardScript(false)).toBool();
2673 }
2674 
canInspect() const2675 bool QtWebKitWebWidget::canInspect() const
2676 {
2677 	return !Utils::isUrlEmpty(getUrl());
2678 }
2679 
canTakeScreenshot() const2680 bool QtWebKitWebWidget::canTakeScreenshot() const
2681 {
2682 	return true;
2683 }
2684 
canRedo() const2685 bool QtWebKitWebWidget::canRedo() const
2686 {
2687 	return m_page->undoStack()->canRedo();
2688 }
2689 
canUndo() const2690 bool QtWebKitWebWidget::canUndo() const
2691 {
2692 	return m_page->undoStack()->canUndo();
2693 }
2694 
canShowContextMenu(const QPoint & position) const2695 bool QtWebKitWebWidget::canShowContextMenu(const QPoint &position) const
2696 {
2697 	if (!getOption(SettingsManager::Permissions_ScriptsCanReceiveRightClicksOption).toBool())
2698 	{
2699 		return true;
2700 	}
2701 
2702 	QContextMenuEvent menuEvent(QContextMenuEvent::Other, position, m_webView->mapToGlobal(position), Qt::NoModifier);
2703 
2704 	return !m_page->swallowContextMenuEvent(&menuEvent);
2705 }
2706 
canViewSource() const2707 bool QtWebKitWebWidget::canViewSource() const
2708 {
2709 	return (!m_page->isDisplayingErrorPage() && !m_page->isViewingMedia() && !Utils::isUrlEmpty(getUrl()));
2710 }
2711 
hasSelection() const2712 bool QtWebKitWebWidget::hasSelection() const
2713 {
2714 	return (m_page->hasSelection() && !m_page->selectedText().isEmpty());
2715 }
2716 
hasWatchedChanges(ChangeWatcher watcher) const2717 bool QtWebKitWebWidget::hasWatchedChanges(ChangeWatcher watcher) const
2718 {
2719 	Q_UNUSED(watcher)
2720 
2721 	return true;
2722 }
2723 
isAudible() const2724 bool QtWebKitWebWidget::isAudible() const
2725 {
2726 	return m_page->recentlyAudible();
2727 }
2728 
isAudioMuted() const2729 bool QtWebKitWebWidget::isAudioMuted() const
2730 {
2731 	return m_isAudioMuted;
2732 }
2733 
isFullScreen() const2734 bool QtWebKitWebWidget::isFullScreen() const
2735 {
2736 	return m_isFullScreen;
2737 }
2738 
isInspecting() const2739 bool QtWebKitWebWidget::isInspecting() const
2740 {
2741 	return (m_inspector && m_inspector->isVisible());
2742 }
2743 
isNavigating() const2744 bool QtWebKitWebWidget::isNavigating() const
2745 {
2746 	return m_isNavigating;
2747 }
2748 
isPopup() const2749 bool QtWebKitWebWidget::isPopup() const
2750 {
2751 	return m_page->isPopup();
2752 }
2753 
isPrivate() const2754 bool QtWebKitWebWidget::isPrivate() const
2755 {
2756 	return m_page->settings()->testAttribute(QWebSettings::PrivateBrowsingEnabled);
2757 }
2758 
isScrollBar(const QPoint & position) const2759 bool QtWebKitWebWidget::isScrollBar(const QPoint &position) const
2760 {
2761 	return (m_page->mainFrame()->scrollBarGeometry(Qt::Horizontal).contains(position) || m_page->mainFrame()->scrollBarGeometry(Qt::Vertical).contains(position));
2762 }
2763 
eventFilter(QObject * object,QEvent * event)2764 bool QtWebKitWebWidget::eventFilter(QObject *object, QEvent *event)
2765 {
2766 	if (object == m_webView)
2767 	{
2768 		const QMouseEvent *mouseEvent(static_cast<QMouseEvent*>(event));
2769 
2770 		if (event->type() == QEvent::MouseButtonPress && mouseEvent && mouseEvent->button() == Qt::LeftButton && SettingsManager::getOption(SettingsManager::Permissions_EnablePluginsOption, Utils::extractHost(getUrl())).toString() == QLatin1String("onDemand"))
2771 		{
2772 			const QWidget *widget(childAt(mouseEvent->pos()));
2773 
2774 			if (widget && widget->metaObject()->className() == QLatin1String("Otter::QtWebKitPluginWidget"))
2775 			{
2776 				QWebElement element(m_page->mainFrame()->hitTestContent(mouseEvent->pos()).element());
2777 				const QString tagName(element.tagName().toLower());
2778 
2779 				if (tagName == QLatin1String("object") || tagName == QLatin1String("embed"))
2780 				{
2781 					m_pluginToken = QUuid::createUuid().toString();
2782 
2783 					QWebElement clonedElement(element.clone());
2784 					clonedElement.setAttribute(QLatin1String("data-otter-browser"), m_pluginToken);
2785 
2786 					element.replace(clonedElement);
2787 
2788 					return true;
2789 				}
2790 			}
2791 		}
2792 
2793 		switch (event->type())
2794 		{
2795 			case QEvent::ChildAdded:
2796 			case QEvent::ChildRemoved:
2797 				if (!m_canLoadPlugins && m_loadingState == FinishedLoadingState)
2798 				{
2799 					updateAmountOfDeferredPlugins();
2800 				}
2801 
2802 				break;
2803 			case QEvent::ContextMenu:
2804 				{
2805 					const QContextMenuEvent *contextMenuEvent(static_cast<QContextMenuEvent*>(event));
2806 
2807 					if (contextMenuEvent && contextMenuEvent->reason() != QContextMenuEvent::Mouse)
2808 					{
2809 						triggerAction(ActionsManager::ContextMenuAction, {{QLatin1String("context"), contextMenuEvent->reason()}});
2810 					}
2811 
2812 					if (!getOption(SettingsManager::Permissions_ScriptsCanReceiveRightClicksOption).toBool())
2813 					{
2814 						return true;
2815 					}
2816 				}
2817 
2818 				break;
2819 			case QEvent::KeyPress:
2820 				{
2821 					const QKeyEvent *keyEvent(static_cast<QKeyEvent*>(event));
2822 
2823 					if (keyEvent->key() == Qt::Key_Escape && m_page->hasSelection())
2824 					{
2825 						event->ignore();
2826 
2827 						return true;
2828 					}
2829 				}
2830 
2831 				break;
2832 			case QEvent::MouseButtonDblClick:
2833 			case QEvent::MouseButtonPress:
2834 			case QEvent::Wheel:
2835 				{
2836 					if (mouseEvent)
2837 					{
2838 						setClickPosition(mouseEvent->pos());
2839 						updateHitTestResult(mouseEvent->pos());
2840 					}
2841 
2842 					QVector<GesturesManager::GesturesContext> contexts;
2843 
2844 					if (getCurrentHitTestResult().flags.testFlag(HitTestResult::IsContentEditableTest))
2845 					{
2846 						contexts.append(GesturesManager::ContentEditableContext);
2847 					}
2848 
2849 					if (getCurrentHitTestResult().linkUrl.isValid())
2850 					{
2851 						contexts.append(GesturesManager::LinkContext);
2852 					}
2853 
2854 					contexts.append(GesturesManager::GenericContext);
2855 
2856 					if ((!mouseEvent || !isScrollBar(mouseEvent->pos())) && GesturesManager::startGesture(object, event, contexts))
2857 					{
2858 						return true;
2859 					}
2860 
2861 					if (event->type() == QEvent::MouseButtonDblClick && mouseEvent->button() == Qt::LeftButton && SettingsManager::getOption(SettingsManager::Browser_ShowSelectionContextMenuOnDoubleClickOption).toBool())
2862 					{
2863 						const HitTestResult hitResult(getHitTestResult(mouseEvent->pos()));
2864 
2865 						if (!hitResult.flags.testFlag(HitTestResult::IsContentEditableTest) && hitResult.tagName != QLatin1String("textarea") && hitResult.tagName!= QLatin1String("select") && hitResult.tagName != QLatin1String("input"))
2866 						{
2867 							setClickPosition(mouseEvent->pos());
2868 
2869 							QTimer::singleShot(250, this, [&]()
2870 							{
2871 								showContextMenu();
2872 							});
2873 						}
2874 					}
2875 				}
2876 
2877 				break;
2878 			case QEvent::MouseButtonRelease:
2879 				if (mouseEvent && mouseEvent->button() == Qt::MiddleButton && !isScrollBar(mouseEvent->pos()) && !getCurrentHitTestResult().flags.testFlag(HitTestResult::IsContentEditableTest))
2880 				{
2881 					return true;
2882 				}
2883 
2884 				break;
2885 			case QEvent::MouseMove:
2886 				event->ignore();
2887 
2888 				return QObject::eventFilter(object, event);
2889 			case QEvent::Move:
2890 			case QEvent::Resize:
2891 				emit geometryChanged();
2892 
2893 				break;
2894 			case QEvent::ShortcutOverride:
2895 				{
2896 					const QString tagName(m_page->mainFrame()->findFirstElement(QLatin1String("*:focus")).tagName().toLower());
2897 
2898 					if (tagName == QLatin1String("object") || tagName == QLatin1String("embed"))
2899 					{
2900 						event->accept();
2901 
2902 						return true;
2903 					}
2904 
2905 					const QKeyEvent *keyEvent(static_cast<QKeyEvent*>(event));
2906 
2907 					if (keyEvent->modifiers() == Qt::ControlModifier)
2908 					{
2909 						switch (keyEvent->key())
2910 						{
2911 							case Qt::Key_Backspace:
2912 							case Qt::Key_Left:
2913 							case Qt::Key_Right:
2914 								if (m_page->currentFrame()->hitTestContent(m_page->inputMethodQuery(Qt::ImCursorRectangle).toRect().center()).isContentEditable())
2915 								{
2916 									event->accept();
2917 								}
2918 
2919 								break;
2920 							default:
2921 								break;
2922 						}
2923 
2924 						return true;
2925 					}
2926 
2927 					if ((keyEvent->modifiers().testFlag(Qt::AltModifier) || keyEvent->modifiers().testFlag(Qt::GroupSwitchModifier)) && m_page->currentFrame()->hitTestContent(m_page->inputMethodQuery(Qt::ImCursorRectangle).toRect().center()).isContentEditable())
2928 					{
2929 						event->accept();
2930 
2931 						return true;
2932 					}
2933 				}
2934 
2935 				break;
2936 			case QEvent::ToolTip:
2937 				handleToolTipEvent(static_cast<QHelpEvent*>(event), m_webView);
2938 
2939 				return true;
2940 			default:
2941 				break;
2942 		}
2943 	}
2944 
2945 	return QObject::eventFilter(object, event);
2946 }
2947 
2948 }
2949