1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2015 - 2021 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 **************************************************************************/
19 
20 #include "QtWebEngineWebWidget.h"
21 #include "QtWebEnginePage.h"
22 #if QTWEBENGINECORE_VERSION >= 0x050D00
23 #include "QtWebEngineUrlRequestInterceptor.h"
24 #endif
25 #include "../../../../core/Application.h"
26 #include "../../../../core/BookmarksManager.h"
27 #include "../../../../core/Console.h"
28 #include "../../../../core/GesturesManager.h"
29 #include "../../../../core/NetworkManager.h"
30 #include "../../../../core/NetworkManagerFactory.h"
31 #include "../../../../core/NotesManager.h"
32 #include "../../../../core/SearchEnginesManager.h"
33 #include "../../../../core/ThemesManager.h"
34 #include "../../../../core/TransfersManager.h"
35 #include "../../../../core/UserScript.h"
36 #include "../../../../core/Utils.h"
37 #include "../../../../core/WebBackend.h"
38 #include "../../../../ui/AuthenticationDialog.h"
39 #include "../../../../ui/ContentsDialog.h"
40 #include "../../../../ui/ContentsWidget.h"
41 #include "../../../../ui/ImagePropertiesDialog.h"
42 #include "../../../../ui/MainWindow.h"
43 #include "../../../../ui/SearchEnginePropertiesDialog.h"
44 #include "../../../../ui/SourceViewerWebWidget.h"
45 #include "../../../../ui/WebsitePreferencesDialog.h"
46 
47 #include <QtCore/QEventLoop>
48 #include <QtCore/QFileInfo>
49 #include <QtCore/QMimeData>
50 #include <QtCore/QTimer>
51 #include <QtGui/QClipboard>
52 #include <QtGui/QContextMenuEvent>
53 #include <QtGui/QImageWriter>
54 #if QTWEBENGINECORE_VERSION >= 0x050C00
55 #include <QtPrintSupport/QPrintPreviewDialog>
56 #endif
57 #include <QtWebEngineCore/QWebEngineCookieStore>
58 #include <QtWebEngineWidgets/QWebEngineHistory>
59 #include <QtWebEngineWidgets/QWebEngineProfile>
60 #include <QtWebEngineWidgets/QWebEngineScript>
61 #include <QtWebEngineWidgets/QWebEngineSettings>
62 #include <QtWidgets/QAction>
63 #include <QtWidgets/QVBoxLayout>
64 
65 namespace Otter
66 {
67 
68 #if QTWEBENGINECORE_VERSION >= 0x050B00
QtWebEngineInspectorWidget(QWebEnginePage * page,QWidget * parent)69 QtWebEngineInspectorWidget::QtWebEngineInspectorWidget(QWebEnginePage *page, QWidget *parent) : QWebEngineView(parent),
70 	m_page(page)
71 {
72 	setMinimumHeight(200);
73 }
74 
showEvent(QShowEvent * event)75 void QtWebEngineInspectorWidget::showEvent(QShowEvent *event)
76 {
77 	if (!page()->inspectedPage())
78 	{
79 		page()->setInspectedPage(m_page);
80 	}
81 
82 	QWebEngineView::showEvent(event);
83 }
84 #endif
85 
QtWebEngineWebWidget(const QVariantMap & parameters,WebBackend * backend,ContentsWidget * parent)86 QtWebEngineWebWidget::QtWebEngineWebWidget(const QVariantMap &parameters, WebBackend *backend, ContentsWidget *parent) : WebWidget(parameters, backend, parent),
87 	m_webView(nullptr),
88 #if QTWEBENGINECORE_VERSION >= 0x050B00
89 	m_inspectorWidget(nullptr),
90 #endif
91 	m_page(new QtWebEnginePage(SessionsManager::calculateOpenHints(parameters).testFlag(SessionsManager::PrivateOpen), this)),
92 #if QTWEBENGINECORE_VERSION >= 0x050D00
93 	m_requestInterceptor(new QtWebEngineUrlRequestInterceptor(this)),
94 #endif
95 	m_loadingState(FinishedLoadingState),
96 	m_canGoForwardValue(UnknownValue),
97 	m_documentLoadingProgress(0),
98 	m_focusProxyTimer(0),
99 	m_updateNavigationActionsTimer(0),
100 	m_isClosing(false),
101 	m_isEditing(false),
102 	m_isFullScreen(false),
103 	m_isTypedIn(false)
104 {
105 	setFocusPolicy(Qt::StrongFocus);
106 
107 #if QTWEBENGINECORE_VERSION >= 0x050D00
108 	m_page->setUrlRequestInterceptor(m_requestInterceptor);
109 #endif
110 
111 	connect(m_page, &QtWebEnginePage::loadProgress, [&](int progress)
112 	{
113 		m_documentLoadingProgress = progress;
114 
115 		emit pageInformationChanged(DocumentLoadingProgressInformation, progress);
116 	});
117 	connect(m_page, &QtWebEnginePage::loadStarted, this, &QtWebEngineWebWidget::handleLoadStarted);
118 	connect(m_page, &QtWebEnginePage::loadFinished, this, &QtWebEngineWebWidget::handleLoadFinished);
119 	connect(m_page, &QtWebEnginePage::linkHovered, this, &QtWebEngineWebWidget::setStatusMessageOverride);
120 	connect(m_page, &QtWebEnginePage::iconChanged, this, &QtWebEngineWebWidget::notifyIconChanged);
121 	connect(m_page, &QtWebEnginePage::requestedPopupWindow, this, &QtWebEngineWebWidget::requestedPopupWindow);
122 	connect(m_page, &QtWebEnginePage::aboutToNavigate, this, &QtWebEngineWebWidget::aboutToNavigate);
123 	connect(m_page, &QtWebEnginePage::requestedNewWindow, this, &QtWebEngineWebWidget::requestedNewWindow);
124 	connect(m_page, &QtWebEnginePage::authenticationRequired, this, &QtWebEngineWebWidget::handleAuthenticationRequired);
125 	connect(m_page, &QtWebEnginePage::proxyAuthenticationRequired, this, &QtWebEngineWebWidget::handleProxyAuthenticationRequired);
126 #if QTWEBENGINECORE_VERSION >= 0x050C00
127 	connect(m_page, &QtWebEnginePage::printRequested, this, &QtWebEngineWebWidget::handlePrintRequest);
128 #endif
129 	connect(m_page, &QtWebEnginePage::windowCloseRequested, this, &QtWebEngineWebWidget::handleWindowCloseRequest);
130 	connect(m_page, &QtWebEnginePage::fullScreenRequested, this, &QtWebEngineWebWidget::handleFullScreenRequest);
131 	connect(m_page, &QtWebEnginePage::featurePermissionRequested, [&](const QUrl &url, QWebEnginePage::Feature feature)
132 	{
133 		notifyPermissionRequested(url, feature, false);
134 	});
135 	connect(m_page, &QtWebEnginePage::featurePermissionRequestCanceled, [&](const QUrl &url, QWebEnginePage::Feature feature)
136 	{
137 		notifyPermissionRequested(url, feature, true);
138 	});
139 	connect(m_page, &QtWebEnginePage::recentlyAudibleChanged, this, &QtWebEngineWebWidget::isAudibleChanged);
140 	connect(m_page, &QtWebEnginePage::viewingMediaChanged, this, &QtWebEngineWebWidget::notifyNavigationActionsChanged);
141 	connect(m_page, &QtWebEnginePage::titleChanged, this, &QtWebEngineWebWidget::notifyTitleChanged);
142 	connect(m_page, &QtWebEnginePage::urlChanged, this, &QtWebEngineWebWidget::notifyUrlChanged);
143 	connect(m_page, &QtWebEnginePage::renderProcessTerminated, this, &QtWebEngineWebWidget::notifyRenderProcessTerminated);
144 	connect(m_page->action(QWebEnginePage::Redo), &QAction::changed, this, &QtWebEngineWebWidget::notifyRedoActionStateChanged);
145 	connect(m_page->action(QWebEnginePage::Undo), &QAction::changed, this, &QtWebEngineWebWidget::notifyUndoActionStateChanged);
146 #if QTWEBENGINECORE_VERSION >= 0x050D00
147 	connect(m_requestInterceptor, &QtWebEngineUrlRequestInterceptor::requestBlocked, this, &QtWebEngineWebWidget::requestBlocked);
148 #endif
149 }
150 
~QtWebEngineWebWidget()151 QtWebEngineWebWidget::~QtWebEngineWebWidget()
152 {
153 	m_isClosing = true;
154 }
155 
timerEvent(QTimerEvent * event)156 void QtWebEngineWebWidget::timerEvent(QTimerEvent *event)
157 {
158 	if (event->timerId() == m_focusProxyTimer)
159 	{
160 		if (focusWidget())
161 		{
162 			focusWidget()->removeEventFilter(this);
163 			focusWidget()->installEventFilter(this);
164 		}
165 	}
166 	else if (event->timerId() == m_updateNavigationActionsTimer)
167 	{
168 		killTimer(m_updateNavigationActionsTimer);
169 
170 		m_updateNavigationActionsTimer = 0;
171 
172 		emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::NavigationCategory});
173 	}
174 	else
175 	{
176 		WebWidget::timerEvent(event);
177 	}
178 }
179 
showEvent(QShowEvent * event)180 void QtWebEngineWebWidget::showEvent(QShowEvent *event)
181 {
182 	WebWidget::showEvent(event);
183 
184 	ensureInitialized();
185 
186 	if (m_focusProxyTimer == 0)
187 	{
188 		m_focusProxyTimer = startTimer(500);
189 	}
190 }
191 
hideEvent(QHideEvent * event)192 void QtWebEngineWebWidget::hideEvent(QHideEvent *event)
193 {
194 	WebWidget::hideEvent(event);
195 
196 	killTimer(m_focusProxyTimer);
197 
198 	m_focusProxyTimer = 0;
199 }
200 
focusInEvent(QFocusEvent * event)201 void QtWebEngineWebWidget::focusInEvent(QFocusEvent *event)
202 {
203 	WebWidget::focusInEvent(event);
204 
205 	ensureInitialized();
206 
207 	m_webView->setFocus();
208 }
209 
ensureInitialized()210 void QtWebEngineWebWidget::ensureInitialized()
211 {
212 	if (!m_webView)
213 	{
214 		m_webView = new QWebEngineView(this);
215 		m_webView->setPage(m_page);
216 		m_webView->setContextMenuPolicy(Qt::CustomContextMenu);
217 		m_webView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
218 		m_webView->installEventFilter(this);
219 
220 		QVBoxLayout *layout(new QVBoxLayout(this));
221 		layout->addWidget(m_webView);
222 		layout->setContentsMargins(0, 0, 0, 0);
223 
224 		setLayout(layout);
225 	}
226 }
227 
search(const QString & query,const QString & searchEngine)228 void QtWebEngineWebWidget::search(const QString &query, const QString &searchEngine)
229 {
230 	QNetworkRequest request;
231 	QNetworkAccessManager::Operation method;
232 	QByteArray body;
233 
234 	if (SearchEnginesManager::setupSearchQuery(query, searchEngine, &request, &method, &body))
235 	{
236 		setRequestedUrl(request.url(), false, true);
237 		updateOptions(request.url());
238 
239 		if (method == QNetworkAccessManager::PostOperation)
240 		{
241 			QWebEngineHttpRequest httpRequest(request.url(), QWebEngineHttpRequest::Post);
242 			httpRequest.setPostData(body);
243 
244 			m_page->load(httpRequest);
245 		}
246 		else
247 		{
248 			setUrl(request.url(), false);
249 		}
250 	}
251 }
252 
print(QPrinter * printer)253 void QtWebEngineWebWidget::print(QPrinter *printer)
254 {
255 	QEventLoop eventLoop;
256 
257 	m_page->print(printer, [&](bool)
258 	{
259 		eventLoop.quit();
260 	});
261 
262 	eventLoop.exec();
263 }
264 
clearOptions()265 void QtWebEngineWebWidget::clearOptions()
266 {
267 	WebWidget::clearOptions();
268 
269 	updateOptions(getUrl());
270 }
271 
triggerAction(int identifier,const QVariantMap & parameters,ActionsManager::TriggerType trigger)272 void QtWebEngineWebWidget::triggerAction(int identifier, const QVariantMap &parameters, ActionsManager::TriggerType trigger)
273 {
274 	switch (identifier)
275 	{
276 		case ActionsManager::SaveAction:
277 			if (m_page->isViewingMedia())
278 			{
279 				const SaveInformation information(Utils::getSavePath(suggestSaveFileName(SingleFileSaveFormat)));
280 
281 				if (information.canSave)
282 				{
283 					m_page->save(information.path, QWebEngineDownloadItem::SingleHtmlSaveFormat);
284 				}
285 			}
286 			else
287 			{
288 				SaveFormat format(UnknownSaveFormat);
289 				const QString path(getSavePath({SingleFileSaveFormat, CompletePageSaveFormat, MhtmlSaveFormat, PdfSaveFormat}, &format));
290 
291 				if (!path.isEmpty())
292 				{
293 					switch (format)
294 					{
295 						case CompletePageSaveFormat:
296 							m_page->save(path, QWebEngineDownloadItem::CompleteHtmlSaveFormat);
297 
298 							break;
299 						case MhtmlSaveFormat:
300 							m_page->save(path, QWebEngineDownloadItem::MimeHtmlSaveFormat);
301 
302 							break;
303 						case PdfSaveFormat:
304 							m_page->printToPdf(path);
305 
306 							break;
307 						default:
308 							m_page->save(path, QWebEngineDownloadItem::SingleHtmlSaveFormat);
309 
310 							break;
311 					}
312 				}
313 			}
314 
315 			break;
316 		case ActionsManager::ClearTabHistoryAction:
317 			setUrl(QUrl(QLatin1String("about:blank")));
318 
319 			m_page->history()->clear();
320 
321 			notifyNavigationActionsChanged();
322 
323 			break;
324 		case ActionsManager::MuteTabMediaAction:
325 			m_page->setAudioMuted(!m_page->isAudioMuted());
326 
327 			emit arbitraryActionsStateChanged({ActionsManager::MuteTabMediaAction});
328 
329 			break;
330 		case ActionsManager::OpenLinkAction:
331 			{
332 				const SessionsManager::OpenHints hints(SessionsManager::calculateOpenHints(parameters));
333 
334 				if (hints == SessionsManager::DefaultOpen && !getCurrentHitTestResult().flags.testFlag(HitTestResult::IsLinkFromSelectionTest))
335 				{
336 					m_page->runJavaScript(parsePosition(QStringLiteral("var element = ((%1 >= 0) ? document.elementFromPoint((%1 + window.scrollX), (%2 + window.scrollX)) : document.activeElement); if (element) { element.click(); }"), getClickPosition()));
337 
338 					setClickPosition({});
339 				}
340 				else if (m_hitResult.linkUrl.isValid())
341 				{
342 					openUrl(m_hitResult.linkUrl, hints);
343 				}
344 			}
345 
346 			break;
347 		case ActionsManager::OpenLinkInCurrentTabAction:
348 			if (m_hitResult.linkUrl.isValid())
349 			{
350 				openUrl(m_hitResult.linkUrl, SessionsManager::CurrentTabOpen);
351 			}
352 
353 			break;
354 		case ActionsManager::OpenLinkInNewTabAction:
355 			if (m_hitResult.linkUrl.isValid())
356 			{
357 				openUrl(m_hitResult.linkUrl, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen));
358 			}
359 
360 			break;
361 		case ActionsManager::OpenLinkInNewTabBackgroundAction:
362 			if (m_hitResult.linkUrl.isValid())
363 			{
364 				openUrl(m_hitResult.linkUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen));
365 			}
366 
367 			break;
368 		case ActionsManager::OpenLinkInNewWindowAction:
369 			if (m_hitResult.linkUrl.isValid())
370 			{
371 				openUrl(m_hitResult.linkUrl, SessionsManager::calculateOpenHints(SessionsManager::NewWindowOpen));
372 			}
373 
374 			break;
375 		case ActionsManager::OpenLinkInNewWindowBackgroundAction:
376 			if (m_hitResult.linkUrl.isValid())
377 			{
378 				openUrl(m_hitResult.linkUrl, (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen));
379 			}
380 
381 			break;
382 		case ActionsManager::OpenLinkInNewPrivateTabAction:
383 			if (m_hitResult.linkUrl.isValid())
384 			{
385 				openUrl(m_hitResult.linkUrl, (SessionsManager::NewTabOpen | SessionsManager::PrivateOpen));
386 			}
387 
388 			break;
389 		case ActionsManager::OpenLinkInNewPrivateTabBackgroundAction:
390 			if (m_hitResult.linkUrl.isValid())
391 			{
392 				openUrl(m_hitResult.linkUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen));
393 			}
394 
395 			break;
396 		case ActionsManager::OpenLinkInNewPrivateWindowAction:
397 			if (m_hitResult.linkUrl.isValid())
398 			{
399 				openUrl(m_hitResult.linkUrl, (SessionsManager::NewWindowOpen | SessionsManager::PrivateOpen));
400 			}
401 
402 			break;
403 		case ActionsManager::OpenLinkInNewPrivateWindowBackgroundAction:
404 			if (m_hitResult.linkUrl.isValid())
405 			{
406 				openUrl(m_hitResult.linkUrl, (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen));
407 			}
408 
409 			break;
410 		case ActionsManager::CopyLinkToClipboardAction:
411 			if (!m_hitResult.linkUrl.isEmpty())
412 			{
413 				Application::clipboard()->setText(m_hitResult.linkUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
414 			}
415 
416 			break;
417 		case ActionsManager::BookmarkLinkAction:
418 			if (m_hitResult.linkUrl.isValid())
419 			{
420 				Application::triggerAction(ActionsManager::BookmarkPageAction, {{QLatin1String("url"), m_hitResult.linkUrl}, {QLatin1String("title"), m_hitResult.title}}, parentWidget(), trigger);
421 			}
422 
423 			break;
424 		case ActionsManager::SaveLinkToDiskAction:
425 			startTransfer(new Transfer(m_hitResult.linkUrl.toString(), {}, (Transfer::CanNotifyOption | (isPrivate() ? Transfer::IsPrivateOption : Transfer::NoOption))));
426 
427 			break;
428 		case ActionsManager::SaveLinkToDownloadsAction:
429 			TransfersManager::addTransfer(new Transfer(m_hitResult.linkUrl.toString(), {}, (Transfer::CanNotifyOption | Transfer::CanAskForPathOption | Transfer::IsQuickTransferOption | (isPrivate() ? Transfer::IsPrivateOption : Transfer::NoOption))));
430 
431 			break;
432 		case ActionsManager::OpenFrameAction:
433 			if (m_hitResult.frameUrl.isValid())
434 			{
435 				openUrl(m_hitResult.frameUrl, SessionsManager::calculateOpenHints(parameters));
436 			}
437 
438 			break;
439 		case ActionsManager::OpenFrameInCurrentTabAction:
440 			if (m_hitResult.frameUrl.isValid())
441 			{
442 				setUrl(m_hitResult.frameUrl, false);
443 			}
444 
445 			break;
446 		case ActionsManager::OpenFrameInNewTabAction:
447 			if (m_hitResult.frameUrl.isValid())
448 			{
449 				openUrl(m_hitResult.frameUrl, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen));
450 			}
451 
452 			break;
453 		case ActionsManager::OpenFrameInNewTabBackgroundAction:
454 			if (m_hitResult.frameUrl.isValid())
455 			{
456 				openUrl(m_hitResult.frameUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen));
457 			}
458 
459 			break;
460 		case ActionsManager::CopyFrameLinkToClipboardAction:
461 			if (m_hitResult.frameUrl.isValid())
462 			{
463 				Application::clipboard()->setText(m_hitResult.frameUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
464 			}
465 
466 			break;
467 		case ActionsManager::ReloadFrameAction:
468 			if (m_hitResult.frameUrl.isValid())
469 			{
470 //TODO Improve
471 				m_page->runJavaScript(QStringLiteral("var frames = document.querySelectorAll('iframe[src=\"%1\"], frame[src=\"%1\"]'); for (var i = 0; i < frames.length; ++i) { frames[i].src = ''; frames[i].src = \'%1\'; }").arg(m_hitResult.frameUrl.toString()));
472 			}
473 
474 			break;
475 		case ActionsManager::ViewFrameSourceAction:
476 			if (m_hitResult.frameUrl.isValid())
477 			{
478 				const QString defaultEncoding(m_page->settings()->defaultTextEncoding());
479 				QNetworkRequest request(m_hitResult.frameUrl);
480 				request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
481 				request.setHeader(QNetworkRequest::UserAgentHeader, m_page->profile()->httpUserAgent());
482 
483 				QNetworkReply *reply(NetworkManagerFactory::getNetworkManager(isPrivate())->get(request));
484 				SourceViewerWebWidget *sourceViewer(new SourceViewerWebWidget(isPrivate()));
485 				sourceViewer->setRequestedUrl(QUrl(QLatin1String("view-source:") + m_hitResult.frameUrl.toString()), false);
486 
487 				if (!defaultEncoding.isEmpty())
488 				{
489 					sourceViewer->setOption(SettingsManager::Content_DefaultCharacterEncodingOption, defaultEncoding);
490 				}
491 
492 				m_viewSourceReplies[reply] = sourceViewer;
493 
494 				connect(reply, &QNetworkReply::finished, this, &QtWebEngineWebWidget::handleViewSourceReplyFinished);
495 
496 				emit requestedNewWindow(sourceViewer, SessionsManager::DefaultOpen, {});
497 			}
498 
499 			break;
500 		case ActionsManager::OpenImageAction:
501 			if (m_hitResult.imageUrl.isValid())
502 			{
503 				openUrl(m_hitResult.imageUrl, SessionsManager::calculateOpenHints(parameters));
504 			}
505 
506 			break;
507 		case ActionsManager::OpenImageInNewTabAction:
508 			if (m_hitResult.imageUrl.isValid())
509 			{
510 				openUrl(m_hitResult.imageUrl, SessionsManager::calculateOpenHints(SessionsManager::NewTabOpen));
511 			}
512 
513 			break;
514 		case ActionsManager::OpenImageInNewTabBackgroundAction:
515 			if (m_hitResult.imageUrl.isValid())
516 			{
517 				openUrl(m_hitResult.imageUrl, (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen));
518 			}
519 
520 			break;
521 		case ActionsManager::SaveImageToDiskAction:
522 			if (m_hitResult.imageUrl.isValid())
523 			{
524 				if (m_hitResult.imageUrl.url().contains(QLatin1String(";base64,")))
525 				{
526 					const QString imageUrl(m_hitResult.imageUrl.url());
527 					const QString imageType(imageUrl.mid(11, (imageUrl.indexOf(QLatin1Char(';')) - 11)));
528 					const QString path(Utils::getSavePath(tr("file") + QLatin1Char('.') + imageType).path);
529 
530 					if (path.isEmpty())
531 					{
532 						break;
533 					}
534 
535 					QImageWriter writer(path);
536 
537 					if (!writer.write(QImage::fromData(QByteArray::fromBase64(imageUrl.mid(imageUrl.indexOf(QLatin1String(";base64,")) + 7).toUtf8()), imageType.toStdString().c_str())))
538 					{
539 						Console::addMessage(tr("Failed to save image: %1").arg(writer.errorString()), Console::OtherCategory, Console::ErrorLevel, path, -1, getWindowIdentifier());
540 					}
541 				}
542 				else
543 				{
544 					QNetworkRequest request(m_hitResult.imageUrl);
545 					request.setHeader(QNetworkRequest::UserAgentHeader, m_page->profile()->httpUserAgent());
546 
547 					new Transfer(request, {}, (Transfer::CanAskForPathOption | Transfer::CanAutoDeleteOption | Transfer::IsPrivateOption));
548 				}
549 			}
550 
551 			break;
552 		case ActionsManager::CopyImageToClipboardAction:
553 			m_page->triggerAction(QWebEnginePage::CopyImageToClipboard);
554 
555 			break;
556 		case ActionsManager::CopyImageUrlToClipboardAction:
557 			if (!m_hitResult.imageUrl.isEmpty())
558 			{
559 				Application::clipboard()->setText(m_hitResult.imageUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
560 			}
561 
562 			break;
563 		case ActionsManager::ReloadImageAction:
564 			if (!m_hitResult.imageUrl.isEmpty())
565 			{
566 				if (getUrl().matches(m_hitResult.imageUrl, (QUrl::NormalizePathSegments | QUrl::RemoveFragment | QUrl::StripTrailingSlash)))
567 				{
568 					triggerAction(ActionsManager::ReloadAndBypassCacheAction, {}, trigger);
569 				}
570 				else
571 				{
572 //TODO Improve
573 					m_page->runJavaScript(QStringLiteral("var images = document.querySelectorAll('img[src=\"%1\"]'); for (var i = 0; i < images.length; ++i) { images[i].src = ''; images[i].src = \'%1\'; }").arg(m_hitResult.imageUrl.toString()));
574 				}
575 			}
576 
577 			break;
578 		case ActionsManager::ImagePropertiesAction:
579 			if (m_hitResult.imageUrl.scheme() == QLatin1String("data"))
580 			{
581 				ImagePropertiesDialog *imagePropertiesDialog(new ImagePropertiesDialog(m_hitResult.imageUrl, {{QLatin1String("alternativeText"), m_hitResult.alternateText}, {QLatin1String("longDescription"), m_hitResult.longDescription}}, nullptr, this));
582 				imagePropertiesDialog->setButtonsVisible(false);
583 
584 				ContentsDialog *dialog(new ContentsDialog(ThemesManager::createIcon(QLatin1String("dialog-information")), imagePropertiesDialog->windowTitle(), {}, {}, QDialogButtonBox::Close, imagePropertiesDialog, this));
585 
586 				connect(this, &QtWebEngineWebWidget::aboutToReload, dialog, &ContentsDialog::close);
587 				connect(imagePropertiesDialog, &ImagePropertiesDialog::finished, dialog, &ContentsDialog::close);
588 
589 				showDialog(dialog, false);
590 			}
591 			else
592 			{
593 				m_page->runJavaScript(parsePosition(QStringLiteral("var element = ((%1 >= 0) ? document.elementFromPoint((%1 + window.scrollX), (%2 + window.scrollX)) : document.activeElement); if (element && element.tagName && element.tagName.toLowerCase() == 'img') { [element.naturalWidth, element.naturalHeight]; }"), getClickPosition()), [&](const QVariant &result)
594 				{
595 					QVariantMap properties({{QLatin1String("alternativeText"), m_hitResult.alternateText}, {QLatin1String("longDescription"), m_hitResult.longDescription}});
596 
597 					if (result.isValid())
598 					{
599 						properties[QLatin1String("width")] = result.toList()[0].toInt();
600 						properties[QLatin1String("height")] = result.toList()[1].toInt();
601 					}
602 
603 					ImagePropertiesDialog *imagePropertiesDialog(new ImagePropertiesDialog(m_hitResult.imageUrl, properties, nullptr, this));
604 					imagePropertiesDialog->setButtonsVisible(false);
605 
606 					ContentsDialog dialog(ThemesManager::createIcon(QLatin1String("dialog-information")), imagePropertiesDialog->windowTitle(), {}, {}, QDialogButtonBox::Close, imagePropertiesDialog, this);
607 
608 					connect(this, &QtWebEngineWebWidget::aboutToReload, &dialog, &ContentsDialog::close);
609 
610 					showDialog(&dialog);
611 				});
612 			}
613 
614 			break;
615 		case ActionsManager::SaveMediaToDiskAction:
616 			if (m_hitResult.mediaUrl.isValid())
617 			{
618 				QNetworkRequest request(m_hitResult.mediaUrl);
619 				request.setHeader(QNetworkRequest::UserAgentHeader, m_page->profile()->httpUserAgent());
620 
621 				new Transfer(request, {}, (Transfer::CanAskForPathOption | Transfer::CanAutoDeleteOption | Transfer::IsPrivateOption));
622 			}
623 
624 			break;
625 		case ActionsManager::CopyMediaUrlToClipboardAction:
626 			if (!m_hitResult.mediaUrl.isEmpty())
627 			{
628 				Application::clipboard()->setText(m_hitResult.mediaUrl.toString(QUrl::EncodeReserved | QUrl::EncodeSpaces));
629 			}
630 
631 			break;
632 		case ActionsManager::MediaControlsAction:
633 			m_page->triggerAction(QWebEnginePage::ToggleMediaControls, parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool());
634 
635 			break;
636 		case ActionsManager::MediaLoopAction:
637 			m_page->triggerAction(QWebEnginePage::ToggleMediaLoop, parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool());
638 
639 			break;
640 		case ActionsManager::MediaPlayPauseAction:
641 			m_page->triggerAction(QWebEnginePage::ToggleMediaPlayPause);
642 
643 			break;
644 		case ActionsManager::MediaMuteAction:
645 			m_page->triggerAction(QWebEnginePage::ToggleMediaMute);
646 
647 			break;
648 		case ActionsManager::MediaPlaybackRateAction:
649 			m_page->runJavaScript(parsePosition(QStringLiteral("var element = ((%1 >= 0) ? document.elementFromPoint((%1 + window.scrollX), (%2 + window.scrollX)) : document.activeElement); if (element) { element.playbackRate = %3; }"), getClickPosition()).arg(parameters.value(QLatin1String("rate"), 1).toReal()));
650 
651 			break;
652 		case ActionsManager::GoBackAction:
653 			m_page->triggerAction(QWebEnginePage::Back);
654 
655 			break;
656 		case ActionsManager::GoForwardAction:
657 			m_page->triggerAction(QWebEnginePage::Forward);
658 
659 			break;
660 		case ActionsManager::GoToHistoryIndexAction:
661 			if (parameters.contains(QLatin1String("index")))
662 			{
663 				const int index(parameters[QLatin1String("index")].toInt());
664 
665 				if (index >= 0 && index < m_page->history()->count())
666 				{
667 					m_page->history()->goToItem(m_page->history()->itemAt(index));
668 				}
669 			}
670 
671 			break;
672 		case ActionsManager::RewindAction:
673 			m_page->history()->goToItem(m_page->history()->itemAt(0));
674 
675 			break;
676 		case ActionsManager::FastForwardAction:
677 			m_page->runJavaScript(getFastForwardScript(true), [&](const QVariant &result)
678 			{
679 				const QUrl url(result.toUrl());
680 
681 				if (url.isValid())
682 				{
683 					setUrl(url);
684 				}
685 				else if (canGoForward())
686 				{
687 					m_page->triggerAction(QWebEnginePage::Forward);
688 				}
689 			});
690 
691 			break;
692 		case ActionsManager::RemoveHistoryIndexAction:
693 			if (parameters.contains(QLatin1String("index")))
694 			{
695 				const int index(parameters[QLatin1String("index")].toInt());
696 
697 				if (index >= 0 && index < m_page->history()->count())
698 				{
699 					WindowHistoryInformation history(getHistory());
700 					history.entries.removeAt(index);
701 
702 					if (history.index >= index)
703 					{
704 						history.index = (history.index - 1);
705 					}
706 
707 					setHistory(history);
708 				}
709 			}
710 
711 			break;
712 		case ActionsManager::StopAction:
713 			m_page->triggerAction(QWebEnginePage::Stop);
714 
715 			break;
716 		case ActionsManager::ReloadAction:
717 			emit aboutToReload();
718 
719 			m_page->triggerAction(QWebEnginePage::Stop);
720 			m_page->triggerAction(QWebEnginePage::Reload);
721 
722 			break;
723 		case ActionsManager::ReloadOrStopAction:
724 			if (m_loadingState == OngoingLoadingState)
725 			{
726 				triggerAction(ActionsManager::StopAction, {}, trigger);
727 			}
728 			else
729 			{
730 				triggerAction(ActionsManager::ReloadAction, {}, trigger);
731 			}
732 
733 			break;
734 		case ActionsManager::ReloadAndBypassCacheAction:
735 			m_page->triggerAction(QWebEnginePage::ReloadAndBypassCache);
736 
737 			break;
738 		case ActionsManager::ContextMenuAction:
739 			showContextMenu(getClickPosition());
740 
741 			break;
742 		case ActionsManager::UndoAction:
743 			m_page->triggerAction(QWebEnginePage::Undo);
744 
745 			break;
746 		case ActionsManager::RedoAction:
747 			m_page->triggerAction(QWebEnginePage::Redo);
748 
749 			break;
750 		case ActionsManager::CutAction:
751 			m_page->triggerAction(QWebEnginePage::Cut);
752 
753 			break;
754 		case ActionsManager::CopyAction:
755 			if (parameters.value(QLatin1String("mode")) == QLatin1String("plainText"))
756 			{
757 				const QString text(getSelectedText());
758 
759 				if (!text.isEmpty())
760 				{
761 					Application::clipboard()->setText(text);
762 				}
763 			}
764 			else
765 			{
766 				m_page->triggerAction(QWebEnginePage::Copy);
767 			}
768 
769 			break;
770 		case ActionsManager::CopyPlainTextAction:
771 			triggerAction(ActionsManager::CopyAction, {{QLatin1String("mode"), QLatin1String("plainText")}}, trigger);
772 
773 			break;
774 		case ActionsManager::CopyAddressAction:
775 			Application::clipboard()->setText(getUrl().toString());
776 
777 			break;
778 		case ActionsManager::CopyToNoteAction:
779 			NotesManager::addNote(BookmarksModel::UrlBookmark, {{BookmarksModel::UrlRole, getUrl()}, {BookmarksModel::DescriptionRole, getSelectedText()}});
780 
781 			break;
782 		case ActionsManager::PasteAction:
783 			if (parameters.contains(QLatin1String("note")))
784 			{
785 				const BookmarksModel::Bookmark *bookmark(NotesManager::getModel()->getBookmark(parameters[QLatin1String("note")].toULongLong()));
786 
787 				if (bookmark)
788 				{
789 					triggerAction(ActionsManager::PasteAction, {{QLatin1String("text"), bookmark->getDescription()}}, trigger);
790 				}
791 			}
792 			else if (parameters.contains(QLatin1String("text")))
793 			{
794 				QMimeData *mimeData(new QMimeData());
795 				const QStringList mimeTypes(Application::clipboard()->mimeData()->formats());
796 
797 				for (int i = 0; i < mimeTypes.count(); ++i)
798 				{
799 					mimeData->setData(mimeTypes.at(i), Application::clipboard()->mimeData()->data(mimeTypes.at(i)));
800 				}
801 
802 				Application::clipboard()->setText(parameters[QLatin1String("text")].toString());
803 
804 				m_page->triggerAction(QWebEnginePage::Paste);
805 
806 				Application::clipboard()->setMimeData(mimeData);
807 			}
808 			else
809 			{
810 				m_page->triggerAction(QWebEnginePage::Paste);
811 			}
812 
813 			break;
814 		case ActionsManager::DeleteAction:
815 			m_page->runJavaScript(QLatin1String("window.getSelection().deleteFromDocument()"));
816 
817 			break;
818 		case ActionsManager::SelectAllAction:
819 			m_page->triggerAction(QWebEnginePage::SelectAll);
820 
821 			break;
822 		case ActionsManager::UnselectAction:
823 			m_page->triggerAction(QWebEnginePage::Unselect);
824 
825 			break;
826 		case ActionsManager::ClearAllAction:
827 			triggerAction(ActionsManager::SelectAllAction, {}, trigger);
828 			triggerAction(ActionsManager::DeleteAction, {}, trigger);
829 
830 			break;
831 		case ActionsManager::CreateSearchAction:
832 			{
833 				QFile file(QLatin1String(":/modules/backends/web/qtwebengine/resources/createSearch.js"));
834 
835 				if (!file.open(QIODevice::ReadOnly))
836 				{
837 					break;
838 				}
839 
840 				m_page->runJavaScript(parsePosition(QString(file.readAll()), getClickPosition()), [&](const QVariant &result)
841 				{
842 					if (result.isNull())
843 					{
844 						return;
845 					}
846 
847 					const QIcon icon(getIcon());
848 					const QUrl url(result.toMap().value(QLatin1String("url")).toString());
849 					SearchEnginesManager::SearchEngineDefinition searchEngine;
850 					searchEngine.title = getTitle();
851 					searchEngine.formUrl = getUrl();
852 					searchEngine.icon = (icon.isNull() ? ThemesManager::createIcon(QLatin1String("edit-find")) : icon);
853 					searchEngine.resultsUrl.url = (url.isEmpty() ? getUrl() : (url.isRelative() ? getUrl().resolved(url) : url)).toString();
854 					searchEngine.resultsUrl.enctype = result.toMap().value(QLatin1String("enctype")).toString();
855 					searchEngine.resultsUrl.method = result.toMap().value(QLatin1String("method")).toString();
856 					searchEngine.resultsUrl.parameters = QUrlQuery(result.toMap().value(QLatin1String("query")).toString());
857 
858 					SearchEnginePropertiesDialog dialog(searchEngine, SearchEnginesManager::getSearchKeywords(), this);
859 
860 					if (dialog.exec() == QDialog::Accepted)
861 					{
862 						SearchEnginesManager::addSearchEngine(dialog.getSearchEngine());
863 					}
864 				});
865 
866 				file.close();
867 			}
868 
869 			break;
870 		case ActionsManager::ScrollToStartAction:
871 			m_page->runJavaScript(QLatin1String("window.scrollTo(0, 0)"));
872 
873 			break;
874 		case ActionsManager::ScrollToEndAction:
875 			m_page->runJavaScript(QLatin1String("window.scrollTo(0, document.body.scrollHeigh)"));
876 
877 			break;
878 		case ActionsManager::ScrollPageUpAction:
879 			m_page->runJavaScript(QLatin1String("window.scrollByPages(1)"));
880 
881 			break;
882 		case ActionsManager::ScrollPageDownAction:
883 			m_page->runJavaScript(QLatin1String("window.scrollByPages(-1)"));
884 
885 			break;
886 		case ActionsManager::ScrollPageLeftAction:
887 			ensureInitialized();
888 
889 			m_page->runJavaScript(QStringLiteral("window.scrollBy(-%1, 0)").arg(m_webView->width()));
890 
891 			break;
892 		case ActionsManager::ScrollPageRightAction:
893 			ensureInitialized();
894 
895 			m_page->runJavaScript(QStringLiteral("window.scrollBy(%1, 0)").arg(m_webView->width()));
896 
897 			break;
898 		case ActionsManager::ActivateContentAction:
899 			ensureInitialized();
900 
901 			m_webView->setFocus();
902 
903 			m_page->runJavaScript(QLatin1String("var element = document.activeElement; if (element && element.tagName && (element.tagName.toLowerCase() == 'input' || element.tagName.toLowerCase() == 'textarea'))) { document.activeElement.blur(); }"));
904 
905 			break;
906 		case ActionsManager::ViewSourceAction:
907 			if (canViewSource())
908 			{
909 				const QString defaultEncoding(m_page->settings()->defaultTextEncoding());
910 				QNetworkRequest request(getUrl());
911 				request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
912 				request.setHeader(QNetworkRequest::UserAgentHeader, m_page->profile()->httpUserAgent());
913 
914 				QNetworkReply *reply(NetworkManagerFactory::getNetworkManager(isPrivate())->get(request));
915 				SourceViewerWebWidget *sourceViewer(new SourceViewerWebWidget(isPrivate()));
916 				sourceViewer->setRequestedUrl(QUrl(QLatin1String("view-source:") + getUrl().toString()), false);
917 
918 				if (!defaultEncoding.isEmpty())
919 				{
920 					sourceViewer->setOption(SettingsManager::Content_DefaultCharacterEncodingOption, defaultEncoding);
921 				}
922 
923 				m_viewSourceReplies[reply] = sourceViewer;
924 
925 				connect(reply, &QNetworkReply::finished, this, &QtWebEngineWebWidget::handleViewSourceReplyFinished);
926 
927 				emit requestedNewWindow(sourceViewer, SessionsManager::DefaultOpen, {});
928 			}
929 
930 			break;
931 #if QTWEBENGINECORE_VERSION >= 0x050B00
932 		case ActionsManager::InspectPageAction:
933 			{
934 				const bool showInspector(parameters.value(QLatin1String("isChecked"), !getActionState(identifier, parameters).isChecked).toBool());
935 
936 				if (showInspector && !m_inspectorWidget)
937 				{
938 					getInspector();
939 				}
940 
941 				emit requestedInspectorVisibilityChange(showInspector);
942 				emit arbitraryActionsStateChanged({ActionsManager::InspectPageAction});
943 			}
944 
945 			break;
946 		case ActionsManager::InspectElementAction:
947 			triggerAction(ActionsManager::InspectPageAction, {{QLatin1String("isChecked"), true}}, trigger);
948 
949 			m_page->triggerAction(QWebEnginePage::InspectElement);
950 
951 			break;
952 #endif
953 		case ActionsManager::FullScreenAction:
954 			{
955 				const MainWindow *mainWindow(MainWindow::findMainWindow(this));
956 
957 				if (mainWindow && !mainWindow->isFullScreen())
958 				{
959 					m_page->triggerAction(QWebEnginePage::ExitFullScreen);
960 				}
961 			}
962 
963 			break;
964 		case ActionsManager::WebsitePreferencesAction:
965 			{
966 				const QUrl url(getUrl());
967 				WebsitePreferencesDialog dialog(Utils::extractHost(url), {}, this);
968 
969 				if (dialog.exec() == QDialog::Accepted)
970 				{
971 					updateOptions(url);
972 
973 					const QVector<QNetworkCookie> cookiesToDelete(dialog.getCookiesToDelete());
974 
975 					for (int i = 0; i < cookiesToDelete.count(); ++i)
976 					{
977 						m_page->profile()->cookieStore()->deleteCookie(cookiesToDelete.at(i));
978 					}
979 
980 					const QVector<QNetworkCookie> cookiesToInsert(dialog.getCookiesToInsert());
981 
982 					for (int i = 0; i < cookiesToInsert.count(); ++i)
983 					{
984 						m_page->profile()->cookieStore()->setCookie(cookiesToInsert.at(i));
985 					}
986 				}
987 			}
988 
989 			break;
990 		default:
991 			break;
992 	}
993 }
994 
handleLoadStarted()995 void QtWebEngineWebWidget::handleLoadStarted()
996 {
997 	m_lastUrlClickTime = {};
998 	m_metaData.clear();
999 	m_styleSheets.clear();
1000 	m_feeds.clear();
1001 	m_links.clear();
1002 	m_searchEngines.clear();
1003 	m_watchedChanges.clear();
1004 	m_loadingState = OngoingLoadingState;
1005 	m_documentLoadingProgress = 0;
1006 
1007 	setStatusMessage({});
1008 	setStatusMessageOverride({});
1009 
1010 	emit geometryChanged();
1011 	emit loadingStateChanged(OngoingLoadingState);
1012 	emit pageInformationChanged(DocumentLoadingProgressInformation, 0);
1013 }
1014 
handleLoadFinished()1015 void QtWebEngineWebWidget::handleLoadFinished()
1016 {
1017 	m_loadingState = FinishedLoadingState;
1018 	m_canGoForwardValue = UnknownValue;
1019 
1020 	notifyNavigationActionsChanged();
1021 	startReloadTimer();
1022 
1023 	QTimer::singleShot(100, this, [&]()
1024 	{
1025 		m_page->runJavaScript(getFastForwardScript(false), [&](const QVariant &result)
1026 		{
1027 			m_canGoForwardValue = (result.toBool() ? TrueValue : FalseValue);
1028 
1029 			emit arbitraryActionsStateChanged({ActionsManager::FastForwardAction});
1030 		});
1031 
1032 		const QVector<ChangeWatcher> watchers({FeedsWatcher, LinksWatcher, MetaDataWatcher, SearchEnginesWatcher, StylesheetsWatcher});
1033 
1034 		for (int i = 0; i < watchers.count(); ++i)
1035 		{
1036 			if (isWatchingChanges(watchers.at(i)))
1037 			{
1038 				updateWatchedData(watchers.at(i));
1039 			}
1040 		}
1041 	});
1042 
1043 	emit contentStateChanged(getContentState());
1044 	emit loadingStateChanged(FinishedLoadingState);
1045 }
1046 
handleViewSourceReplyFinished()1047 void QtWebEngineWebWidget::handleViewSourceReplyFinished()
1048 {
1049 	QNetworkReply *reply(qobject_cast<QNetworkReply*>(sender()));
1050 
1051 	if (reply)
1052 	{
1053 		if (reply->error() == QNetworkReply::NoError && m_viewSourceReplies.contains(reply) && m_viewSourceReplies[reply])
1054 		{
1055 			m_viewSourceReplies[reply]->setContents(reply->readAll(), reply->header(QNetworkRequest::ContentTypeHeader).toString());
1056 		}
1057 
1058 		m_viewSourceReplies.remove(reply);
1059 
1060 		reply->deleteLater();
1061 	}
1062 }
1063 
1064 #if QTWEBENGINECORE_VERSION >= 0x050C00
handlePrintRequest()1065 void QtWebEngineWebWidget::handlePrintRequest()
1066 {
1067 	QPrintPreviewDialog printPreviewDialog(this);
1068 	printPreviewDialog.setWindowFlags(printPreviewDialog.windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint);
1069 	printPreviewDialog.setWindowTitle(tr("Print Preview"));
1070 
1071 	if (Application::getActiveWindow())
1072 	{
1073 		printPreviewDialog.resize(Application::getActiveWindow()->size());
1074 	}
1075 
1076 	connect(&printPreviewDialog, &QPrintPreviewDialog::paintRequested, this, [&](QPrinter *printer)
1077 	{
1078 		QEventLoop eventLoop;
1079 
1080 		m_page->print(printer, [&](bool)
1081 		{
1082 			eventLoop.quit();
1083 		});
1084 
1085 		eventLoop.exec();
1086 	});
1087 
1088 	printPreviewDialog.exec();
1089 }
1090 #endif
1091 
handleAuthenticationRequired(const QUrl & url,QAuthenticator * authenticator)1092 void QtWebEngineWebWidget::handleAuthenticationRequired(const QUrl &url, QAuthenticator *authenticator)
1093 {
1094 	AuthenticationDialog *authenticationDialog(new AuthenticationDialog(url, authenticator, AuthenticationDialog::HttpAuthentication, this));
1095 	authenticationDialog->setButtonsVisible(false);
1096 
1097 	ContentsDialog dialog(ThemesManager::createIcon(QLatin1String("dialog-password")), authenticationDialog->windowTitle(), {}, {}, (QDialogButtonBox::Ok | QDialogButtonBox::Cancel), authenticationDialog, this);
1098 
1099 	connect(&dialog, &ContentsDialog::accepted, authenticationDialog, &AuthenticationDialog::accept);
1100 	connect(this, &QtWebEngineWebWidget::aboutToReload, &dialog, &ContentsDialog::close);
1101 
1102 	showDialog(&dialog);
1103 }
1104 
handleProxyAuthenticationRequired(const QUrl & url,QAuthenticator * authenticator,const QString & proxy)1105 void QtWebEngineWebWidget::handleProxyAuthenticationRequired(const QUrl &url, QAuthenticator *authenticator, const QString &proxy)
1106 {
1107 	Q_UNUSED(url)
1108 
1109 	AuthenticationDialog *authenticationDialog(new AuthenticationDialog(proxy, authenticator, AuthenticationDialog::ProxyAuthentication, this));
1110 	authenticationDialog->setButtonsVisible(false);
1111 
1112 	ContentsDialog dialog(ThemesManager::createIcon(QLatin1String("dialog-password")), authenticationDialog->windowTitle(), {}, {}, (QDialogButtonBox::Ok | QDialogButtonBox::Cancel), authenticationDialog, this);
1113 
1114 	connect(&dialog, &ContentsDialog::accepted, authenticationDialog, &AuthenticationDialog::accept);
1115 	connect(this, &QtWebEngineWebWidget::aboutToReload, &dialog, &ContentsDialog::close);
1116 
1117 	showDialog(&dialog);
1118 }
1119 
handleFullScreenRequest(QWebEngineFullScreenRequest request)1120 void QtWebEngineWebWidget::handleFullScreenRequest(QWebEngineFullScreenRequest request)
1121 {
1122 	request.accept();
1123 
1124 	if (request.toggleOn())
1125 	{
1126 		const QString value(SettingsManager::getOption(SettingsManager::Permissions_EnableFullScreenOption, Utils::extractHost(request.origin())).toString());
1127 
1128 		if (value == QLatin1String("allow"))
1129 		{
1130 			MainWindow *mainWindow(MainWindow::findMainWindow(this));
1131 
1132 			if (mainWindow && !mainWindow->isFullScreen())
1133 			{
1134 				mainWindow->triggerAction(ActionsManager::FullScreenAction);
1135 			}
1136 		}
1137 		else if (value == QLatin1String("ask"))
1138 		{
1139 			emit requestedPermission(FullScreenFeature, request.origin(), false);
1140 		}
1141 	}
1142 	else
1143 	{
1144 		MainWindow *mainWindow(MainWindow::findMainWindow(this));
1145 
1146 		if (mainWindow && mainWindow->isFullScreen())
1147 		{
1148 			mainWindow->triggerAction(ActionsManager::FullScreenAction);
1149 		}
1150 
1151 		emit requestedPermission(FullScreenFeature, request.origin(), true);
1152 	}
1153 
1154 	m_isFullScreen = request.toggleOn();
1155 
1156 	emit isFullScreenChanged(m_isFullScreen);
1157 }
1158 
notifyTitleChanged()1159 void QtWebEngineWebWidget::notifyTitleChanged()
1160 {
1161 	emit titleChanged(getTitle());
1162 }
1163 
notifyUrlChanged()1164 void QtWebEngineWebWidget::notifyUrlChanged()
1165 {
1166 	const QUrl &url(getUrl());
1167 
1168 	notifyNavigationActionsChanged();
1169 	updateOptions(url);
1170 
1171 	emit iconChanged(getIcon());
1172 	emit urlChanged((url.toString() == QLatin1String("about:blank")) ? m_page->requestedUrl() : url);
1173 	emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::PageCategory});
1174 
1175 	SessionsManager::markSessionAsModified();
1176 }
1177 
notifyIconChanged()1178 void QtWebEngineWebWidget::notifyIconChanged()
1179 {
1180 	emit iconChanged(getIcon());
1181 }
1182 
notifyPermissionRequested(const QUrl & url,QWebEnginePage::Feature nativeFeature,bool cancel)1183 void QtWebEngineWebWidget::notifyPermissionRequested(const QUrl &url, QWebEnginePage::Feature nativeFeature, bool cancel)
1184 {
1185 	FeaturePermission feature(UnknownFeature);
1186 
1187 	switch (nativeFeature)
1188 	{
1189 		case QWebEnginePage::Geolocation:
1190 			feature = GeolocationFeature;
1191 
1192 			break;
1193 		case QWebEnginePage::MediaAudioCapture:
1194 			feature = CaptureAudioFeature;
1195 
1196 			break;
1197 		case QWebEnginePage::MediaVideoCapture:
1198 			feature = CaptureVideoFeature;
1199 
1200 			break;
1201 		case QWebEnginePage::MediaAudioVideoCapture:
1202 			feature = CaptureAudioVideoFeature;
1203 
1204 			break;
1205 		case QWebEnginePage::Notifications:
1206 			feature = NotificationsFeature;
1207 
1208 			break;
1209 		case QWebEnginePage::MouseLock:
1210 			feature = PointerLockFeature;
1211 
1212 			break;
1213 		default:
1214 			return;
1215 	}
1216 
1217 	if (cancel)
1218 	{
1219 		emit requestedPermission(feature, url, true);
1220 	}
1221 	else
1222 	{
1223 		switch (getPermission(feature, url))
1224 		{
1225 			case GrantedPermission:
1226 				m_page->setFeaturePermission(url, nativeFeature, QWebEnginePage::PermissionGrantedByUser);
1227 
1228 				break;
1229 			case DeniedPermission:
1230 				m_page->setFeaturePermission(url, nativeFeature, QWebEnginePage::PermissionDeniedByUser);
1231 
1232 				break;
1233 			default:
1234 				emit requestedPermission(feature, url, false);
1235 
1236 				break;
1237 		}
1238 	}
1239 }
1240 
notifyRenderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus status)1241 void QtWebEngineWebWidget::notifyRenderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus status)
1242 {
1243 	if (status != QWebEnginePage::NormalTerminationStatus)
1244 	{
1245 		m_loadingState = CrashedLoadingState;
1246 
1247 		emit loadingStateChanged(CrashedLoadingState);
1248 	}
1249 }
1250 
notifyNavigationActionsChanged()1251 void QtWebEngineWebWidget::notifyNavigationActionsChanged()
1252 {
1253 	if (m_updateNavigationActionsTimer == 0)
1254 	{
1255 		m_updateNavigationActionsTimer = startTimer(0);
1256 	}
1257 }
1258 
notifyWatchedDataChanged(ChangeWatcher watcher)1259 void QtWebEngineWebWidget::notifyWatchedDataChanged(ChangeWatcher watcher)
1260 {
1261 	if (m_isClosing)
1262 	{
1263 		return;
1264 	}
1265 
1266 	if (watcher >= m_watchedChanges.count())
1267 	{
1268 		m_watchedChanges.resize(watcher + 1);
1269 	}
1270 
1271 	m_watchedChanges[watcher] = true;
1272 
1273 	emit watchedDataChanged(watcher);
1274 }
1275 
updateOptions(const QUrl & url)1276 void QtWebEngineWebWidget::updateOptions(const QUrl &url)
1277 {
1278 	const QString encoding(getOption(SettingsManager::Content_DefaultCharacterEncodingOption, url).toString());
1279 	QWebEngineSettings *settings(m_page->settings());
1280 	settings->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, getOption(SettingsManager::Security_AllowMixedContentOption, url).toBool());
1281 	settings->setAttribute(QWebEngineSettings::AutoLoadImages, (getOption(SettingsManager::Permissions_EnableImagesOption, url).toString() != QLatin1String("onlyCached")));
1282 	settings->setAttribute(QWebEngineSettings::JavascriptEnabled, getOption(SettingsManager::Permissions_EnableJavaScriptOption, url).toBool());
1283 	settings->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, getOption(SettingsManager::Permissions_ScriptsCanAccessClipboardOption, url).toBool());
1284 	settings->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, (getOption(SettingsManager::Permissions_ScriptsCanOpenWindowsOption, url).toString() != QLatin1String("blockAll")));
1285 	settings->setAttribute(QWebEngineSettings::LocalStorageEnabled, getOption(SettingsManager::Permissions_EnableLocalStorageOption, url).toBool());
1286 #if QTWEBENGINECORE_VERSION >= 0x050A00
1287 	settings->setAttribute(QWebEngineSettings::ShowScrollBars, getOption(SettingsManager::Interface_ShowScrollBarsOption, url).toBool());
1288 #endif
1289 	settings->setAttribute(QWebEngineSettings::WebGLEnabled, getOption(SettingsManager::Permissions_EnableWebglOption, url).toBool());
1290 	settings->setDefaultTextEncoding((encoding == QLatin1String("auto")) ? QString() : encoding);
1291 
1292 	m_page->profile()->setHttpUserAgent(getBackend()->getUserAgent(NetworkManagerFactory::getUserAgent(getOption(SettingsManager::Network_UserAgentOption, url).toString()).value));
1293 
1294 	disconnect(m_page, &QtWebEnginePage::geometryChangeRequested, this, &QtWebEngineWebWidget::requestedGeometryChange);
1295 
1296 	if (getOption(SettingsManager::Permissions_ScriptsCanChangeWindowGeometryOption, url).toBool())
1297 	{
1298 		connect(m_page, &QtWebEnginePage::geometryChangeRequested, this, &QtWebEngineWebWidget::requestedGeometryChange);
1299 	}
1300 
1301 #if QTWEBENGINECORE_VERSION >= 0x050D00
1302 	m_requestInterceptor->updateOptions(url);
1303 #endif
1304 }
1305 
updateWatchedData(ChangeWatcher watcher)1306 void QtWebEngineWebWidget::updateWatchedData(ChangeWatcher watcher)
1307 {
1308 	switch (watcher)
1309 	{
1310 		case FeedsWatcher:
1311 			m_page->runJavaScript(m_page->createScriptSource(QLatin1String("getLinks"), {QLatin1String("a[type=\\'application/atom+xml\\'], a[type=\\'application/rss+xml\\'], link[type=\\'application/atom+xml\\'], link[type=\\'application/rss+xml\\']")}), [&](const QVariant &result)
1312 			{
1313 				m_feeds = processLinks(result.toList());
1314 
1315 				notifyWatchedDataChanged(FeedsWatcher);
1316 			});
1317 
1318 			break;
1319 		case LinksWatcher:
1320 			m_page->runJavaScript(m_page->createScriptSource(QLatin1String("getLinks"), {QLatin1String("a[href]")}), [&](const QVariant &result)
1321 			{
1322 				m_links = processLinks(result.toList());
1323 
1324 				notifyWatchedDataChanged(LinksWatcher);
1325 			});
1326 
1327 			break;
1328 		case MetaDataWatcher:
1329 			m_page->runJavaScript(QLatin1String("var elements = document.querySelectorAll('meta'); var metaData = []; for (var i = 0; i < elements.length; ++i) { if (elements[i].name !== '') { metaData.push({key: elements[i].name, value: elements[i].content}); } } metaData;"), [&](const QVariant &result)
1330 			{
1331 				QMultiMap<QString, QString> metaData;
1332 				const QVariantList rawMetaData(result.toList());
1333 
1334 				for (int i = 0; i < rawMetaData.count(); ++i)
1335 				{
1336 					const QVariantHash entry(rawMetaData.at(i).toHash());
1337 
1338 					metaData.insertMulti(entry.value(QLatin1String("key")).toString(), entry.value(QLatin1String("value")).toString());
1339 				}
1340 
1341 				m_metaData = metaData;
1342 
1343 				notifyWatchedDataChanged(MetaDataWatcher);
1344 			});
1345 
1346 			break;
1347 		case SearchEnginesWatcher:
1348 			m_page->runJavaScript(m_page->createScriptSource(QLatin1String("getLinks"), {QLatin1String("link[type=\\'application/opensearchdescription+xml\\']")}), [&](const QVariant &result)
1349 			{
1350 				m_searchEngines = processLinks(result.toList());
1351 
1352 				notifyWatchedDataChanged(SearchEnginesWatcher);
1353 			});
1354 
1355 			break;
1356 		case StylesheetsWatcher:
1357 			m_page->runJavaScript(m_page->createScriptSource(QLatin1String("getStyleSheets")), [&](const QVariant &result)
1358 			{
1359 				m_styleSheets = result.toStringList();
1360 
1361 				notifyWatchedDataChanged(StylesheetsWatcher);
1362 			});
1363 
1364 			break;
1365 		default:
1366 			break;
1367 	}
1368 }
1369 
setScrollPosition(const QPoint & position)1370 void QtWebEngineWebWidget::setScrollPosition(const QPoint &position)
1371 {
1372 	m_page->runJavaScript(QStringLiteral("window.scrollTo(%1, %2); [window.scrollX, window.scrollY];").arg(position.x()).arg(position.y()));
1373 }
1374 
setHistory(const WindowHistoryInformation & history)1375 void QtWebEngineWebWidget::setHistory(const WindowHistoryInformation &history)
1376 {
1377 	m_page->setHistory(history);
1378 
1379 	if (history.entries.isEmpty())
1380 	{
1381 		updateOptions({});
1382 	}
1383 	else
1384 	{
1385 		const QUrl url(m_page->history()->itemAt(history.index).url());
1386 
1387 		setRequestedUrl(url, false, true);
1388 		updateOptions(url);
1389 
1390 		m_page->triggerAction(QWebEnginePage::Reload);
1391 	}
1392 
1393 	notifyNavigationActionsChanged();
1394 
1395 	emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::PageCategory});
1396 }
1397 
setHistory(QDataStream & stream)1398 void QtWebEngineWebWidget::setHistory(QDataStream &stream)
1399 {
1400 	stream.device()->reset();
1401 	stream >> *(m_page->history());
1402 
1403 	const QUrl url(m_page->history()->currentItem().url());
1404 
1405 	setRequestedUrl(url, false, true);
1406 	updateOptions(url);
1407 }
1408 
setZoom(int zoom)1409 void QtWebEngineWebWidget::setZoom(int zoom)
1410 {
1411 	if (zoom != getZoom())
1412 	{
1413 		m_page->setZoomFactor(qBound(0.1, (static_cast<qreal>(zoom) / 100), static_cast<qreal>(100)));
1414 
1415 		SessionsManager::markSessionAsModified();
1416 
1417 		emit zoomChanged(zoom);
1418 		emit geometryChanged();
1419 	}
1420 }
1421 
setUrl(const QUrl & url,bool isTyped)1422 void QtWebEngineWebWidget::setUrl(const QUrl &url, bool isTyped)
1423 {
1424 	if (url.scheme() == QLatin1String("javascript"))
1425 	{
1426 		m_page->runJavaScript(url.toDisplayString(QUrl::RemoveScheme | QUrl::DecodeReserved));
1427 	}
1428 	else if (!url.fragment().isEmpty() && url.matches(getUrl(), (QUrl::RemoveFragment | QUrl::StripTrailingSlash | QUrl::NormalizePathSegments)))
1429 	{
1430 		m_page->runJavaScript(QStringLiteral("var element = document.querySelector('a[name=%1], [id=%1]'); if (element) { var geometry = element.getBoundingClientRect(); [(geometry.left + window.scrollX), (geometry.top + window.scrollY)]; }").arg(url.fragment()), [&](const QVariant &result)
1431 		{
1432 			if (result.isValid())
1433 			{
1434 				setScrollPosition(QPoint(result.toList().value(0).toInt(), result.toList().value(1).toInt()));
1435 			}
1436 		});
1437 	}
1438 	else
1439 	{
1440 		m_isTypedIn = isTyped;
1441 
1442 		const QUrl targetUrl(Utils::expandUrl(url));
1443 
1444 		updateOptions(targetUrl);
1445 
1446 		m_page->load(targetUrl);
1447 
1448 		notifyTitleChanged();
1449 		notifyIconChanged();
1450 	}
1451 }
1452 
setActiveStyleSheet(const QString & styleSheet)1453 void QtWebEngineWebWidget::setActiveStyleSheet(const QString &styleSheet)
1454 {
1455 	m_page->runJavaScript(QStringLiteral("var elements = document.querySelectorAll('link[rel=\\'alternate stylesheet\\']'); for (var i = 0; i < elements.length; ++i) { elements[i].disabled = (elements[i].title !== '%1'); }").arg(QString(styleSheet).replace(QLatin1Char('\''), QLatin1String("\\'"))));
1456 }
1457 
setPermission(FeaturePermission feature,const QUrl & url,PermissionPolicies policies)1458 void QtWebEngineWebWidget::setPermission(FeaturePermission feature, const QUrl &url, PermissionPolicies policies)
1459 {
1460 	WebWidget::setPermission(feature, url, policies);
1461 
1462 	if (feature == FullScreenFeature)
1463 	{
1464 		if (policies.testFlag(GrantedPermission))
1465 		{
1466 			MainWindow *mainWindow(MainWindow::findMainWindow(this));
1467 
1468 			if (mainWindow && !mainWindow->isFullScreen())
1469 			{
1470 				mainWindow->triggerAction(ActionsManager::FullScreenAction);
1471 			}
1472 		}
1473 
1474 		return;
1475 	}
1476 
1477 	QWebEnginePage::Feature nativeFeature(QWebEnginePage::Geolocation);
1478 
1479 	switch (feature)
1480 	{
1481 		case GeolocationFeature:
1482 			nativeFeature = QWebEnginePage::Geolocation;
1483 
1484 			break;
1485 		case NotificationsFeature:
1486 			nativeFeature = QWebEnginePage::Notifications;
1487 
1488 			break;
1489 		case PointerLockFeature:
1490 			nativeFeature = QWebEnginePage::MouseLock;
1491 
1492 			break;
1493 		case CaptureAudioFeature:
1494 			nativeFeature = QWebEnginePage::MediaAudioCapture;
1495 
1496 			break;
1497 		case CaptureVideoFeature:
1498 			nativeFeature = QWebEnginePage::MediaVideoCapture;
1499 
1500 			break;
1501 		case CaptureAudioVideoFeature:
1502 			nativeFeature = QWebEnginePage::MediaAudioVideoCapture;
1503 
1504 			break;
1505 		default:
1506 			return;
1507 	}
1508 
1509 	m_page->setFeaturePermission(url, nativeFeature, (policies.testFlag(GrantedPermission) ? QWebEnginePage::PermissionGrantedByUser : QWebEnginePage::PermissionDeniedByUser));
1510 }
1511 
setOption(int identifier,const QVariant & value)1512 void QtWebEngineWebWidget::setOption(int identifier, const QVariant &value)
1513 {
1514 	WebWidget::setOption(identifier, value);
1515 
1516 	updateOptions(getUrl());
1517 
1518 	if (identifier == SettingsManager::Content_DefaultCharacterEncodingOption)
1519 	{
1520 		m_page->triggerAction(QWebEnginePage::Reload);
1521 	}
1522 }
1523 
setOptions(const QHash<int,QVariant> & options,const QStringList & excludedOptions)1524 void QtWebEngineWebWidget::setOptions(const QHash<int, QVariant> &options, const QStringList &excludedOptions)
1525 {
1526 	WebWidget::setOptions(options, excludedOptions);
1527 
1528 	updateOptions(getUrl());
1529 }
1530 
clone(bool cloneHistory,bool isPrivate,const QStringList & excludedOptions) const1531 WebWidget* QtWebEngineWebWidget::clone(bool cloneHistory, bool isPrivate, const QStringList &excludedOptions) const
1532 {
1533 	QtWebEngineWebWidget *widget((this->isPrivate() || isPrivate) ? new QtWebEngineWebWidget({{QLatin1String("hints"), SessionsManager::PrivateOpen}}, getBackend()) : new QtWebEngineWebWidget({}, getBackend()));
1534 	widget->setOptions(getOptions(), excludedOptions);
1535 
1536 	if (cloneHistory)
1537 	{
1538 		QByteArray byteArray;
1539 		QDataStream stream(&byteArray, QIODevice::ReadWrite);
1540 		stream << *(m_page->history());
1541 
1542 		widget->setHistory(stream);
1543 	}
1544 
1545 	widget->setZoom(getZoom());
1546 
1547 	return widget;
1548 }
1549 
1550 #if QTWEBENGINECORE_VERSION >= 0x050B00
getInspector()1551 QWidget* QtWebEngineWebWidget::getInspector()
1552 {
1553 	if (!m_inspectorWidget)
1554 	{
1555 		m_inspectorWidget = new QtWebEngineInspectorWidget(m_page, this);
1556 	}
1557 
1558 	return m_inspectorWidget;
1559 }
1560 #endif
1561 
getViewport()1562 QWidget* QtWebEngineWebWidget::getViewport()
1563 {
1564 	return (focusWidget() ? focusWidget() : m_webView);
1565 }
1566 
getPage() const1567 QWebEnginePage* QtWebEngineWebWidget::getPage() const
1568 {
1569 	return m_page;
1570 }
1571 
parsePosition(const QString & script,const QPoint & position) const1572 QString QtWebEngineWebWidget::parsePosition(const QString &script, const QPoint &position) const
1573 {
1574 	return script.arg(position.x() / m_page->zoomFactor()).arg(position.y() / m_page->zoomFactor());
1575 }
1576 
getTitle() const1577 QString QtWebEngineWebWidget::getTitle() const
1578 {
1579 	const QString title(m_page->title());
1580 
1581 	if (!title.isEmpty())
1582 	{
1583 		return title;
1584 	}
1585 
1586 	const QUrl url(getUrl());
1587 
1588 	if (Utils::isUrlEmpty(url))
1589 	{
1590 		return tr("Blank Page");
1591 	}
1592 
1593 	if (url.isLocalFile())
1594 	{
1595 		return QFileInfo(url.toLocalFile()).canonicalFilePath();
1596 	}
1597 
1598 	if (!url.isEmpty())
1599 	{
1600 		return url.toString();
1601 	}
1602 
1603 	return tr("(Untitled)");
1604 }
1605 
getDescription() const1606 QString QtWebEngineWebWidget::getDescription() const
1607 {
1608 	return m_page->runScriptSource(QLatin1String("var element = document.querySelector('[name=\\'description\\']'); var description = (element ? element.content : ''); if (description == '') { element = document.querySelector('[name=\\'og:description\\']'); description = (element ? element.property : ''); } description;")).toString();
1609 }
1610 
getActiveStyleSheet() const1611 QString QtWebEngineWebWidget::getActiveStyleSheet() const
1612 {
1613 	return m_page->runScriptFile(QLatin1String("getActiveStyleSheet")).toString();
1614 }
1615 
getSelectedText() const1616 QString QtWebEngineWebWidget::getSelectedText() const
1617 {
1618 	return m_page->selectedText();
1619 }
1620 
getPageInformation(PageInformation key) const1621 QVariant QtWebEngineWebWidget::getPageInformation(PageInformation key) const
1622 {
1623 	if (key == DocumentLoadingProgressInformation || key == TotalLoadingProgressInformation)
1624 	{
1625 		return m_documentLoadingProgress;
1626 	}
1627 
1628 	return WebWidget::getPageInformation(key);
1629 }
1630 
getUrl() const1631 QUrl QtWebEngineWebWidget::getUrl() const
1632 {
1633 	const QUrl url(m_page->url());
1634 
1635 	return (Utils::isUrlEmpty(url) ? ((url.toString() == QLatin1String("about:blank#blocked")) ? QUrl(QLatin1String("about:start")) : m_page->requestedUrl()) : url);
1636 }
1637 
getIcon() const1638 QIcon QtWebEngineWebWidget::getIcon() const
1639 {
1640 	if (isPrivate())
1641 	{
1642 		return ThemesManager::createIcon(QLatin1String("tab-private"));
1643 	}
1644 
1645 	const QIcon icon(m_page->icon());
1646 
1647 	return (icon.isNull() ? ThemesManager::createIcon(QLatin1String("tab")) : icon);
1648 }
1649 
getLastUrlClickTime() const1650 QDateTime QtWebEngineWebWidget::getLastUrlClickTime() const
1651 {
1652 	return m_lastUrlClickTime;
1653 }
1654 
1655 #if QTWEBENGINECORE_VERSION >= 0x050D00
getBlockedElements() const1656 QStringList QtWebEngineWebWidget::getBlockedElements() const
1657 {
1658 	return m_requestInterceptor->getBlockedElements();
1659 }
1660 #endif
1661 
getScrollPosition() const1662 QPoint QtWebEngineWebWidget::getScrollPosition() const
1663 {
1664 	return m_page->scrollPosition().toPoint();
1665 }
1666 
getActiveFrame() const1667 WebWidget::LinkUrl QtWebEngineWebWidget::getActiveFrame() const
1668 {
1669 	LinkUrl link;
1670 
1671 	if (!m_hitResult.frameUrl.isEmpty())
1672 	{
1673 		link.title = m_hitResult.title;
1674 		link.url = m_hitResult.frameUrl;
1675 	}
1676 
1677 	return link;
1678 }
1679 
getActiveImage() const1680 WebWidget::LinkUrl QtWebEngineWebWidget::getActiveImage() const
1681 {
1682 	LinkUrl link;
1683 
1684 	if (!m_hitResult.imageUrl.isEmpty())
1685 	{
1686 		link.title = m_hitResult.alternateText;
1687 		link.url = m_hitResult.imageUrl;
1688 	}
1689 
1690 	return link;
1691 }
1692 
getActiveLink() const1693 WebWidget::LinkUrl QtWebEngineWebWidget::getActiveLink() const
1694 {
1695 	LinkUrl link;
1696 
1697 	if (!m_hitResult.linkUrl.isEmpty())
1698 	{
1699 //TODO Extract text?
1700 		link.title = m_hitResult.title;
1701 		link.url = m_hitResult.linkUrl;
1702 	}
1703 
1704 	return link;
1705 }
1706 
getActiveMedia() const1707 WebWidget::LinkUrl QtWebEngineWebWidget::getActiveMedia() const
1708 {
1709 	LinkUrl link;
1710 
1711 	if (!m_hitResult.mediaUrl.isEmpty())
1712 	{
1713 		link.title = m_hitResult.title;
1714 		link.url = m_hitResult.mediaUrl;
1715 	}
1716 
1717 	return link;
1718 }
1719 
getHistory() const1720 WindowHistoryInformation QtWebEngineWebWidget::getHistory() const
1721 {
1722 	return m_page->getHistory();
1723 }
1724 
getHitTestResult(const QPoint & position)1725 WebWidget::HitTestResult QtWebEngineWebWidget::getHitTestResult(const QPoint &position)
1726 {
1727 	m_hitResult = QtWebEngineHitTestResult(m_page->runScriptFile(QLatin1String("hitTest"), {QString::number(position.x() / m_page->zoomFactor()), QString::number(position.y() / m_page->zoomFactor())}));
1728 
1729 	if (m_hitResult.flags.testFlag(HitTestResult::IsSelectedTest) && !m_hitResult.linkUrl.isValid() && Utils::isUrl(m_page->selectedText()))
1730 	{
1731 		m_hitResult.flags |= HitTestResult::IsLinkFromSelectionTest;
1732 		m_hitResult.linkUrl = QUrl::fromUserInput(m_page->selectedText());
1733 	}
1734 
1735 	return m_hitResult;
1736 }
1737 
getStyleSheets() const1738 QStringList QtWebEngineWebWidget::getStyleSheets() const
1739 {
1740 	return m_styleSheets;
1741 }
1742 
processLinks(const QVariantList & rawLinks) const1743 QVector<WebWidget::LinkUrl> QtWebEngineWebWidget::processLinks(const QVariantList &rawLinks) const
1744 {
1745 	QVector<LinkUrl> links;
1746 	links.reserve(rawLinks.count());
1747 
1748 	for (int i = 0; i < rawLinks.count(); ++i)
1749 	{
1750 		const QVariantHash rawLink(rawLinks.at(i).toHash());
1751 		LinkUrl link;
1752 		link.title = rawLink.value(QLatin1String("title")).toString();
1753 		link.mimeType = rawLink.value(QLatin1String("mimeType")).toString();
1754 		link.url = QUrl(rawLink.value(QLatin1String("url")).toString());
1755 
1756 		links.append(link);
1757 	}
1758 
1759 	return links;
1760 }
1761 
getFeeds() const1762 QVector<WebWidget::LinkUrl> QtWebEngineWebWidget::getFeeds() const
1763 {
1764 	return m_feeds;
1765 }
1766 
getLinks() const1767 QVector<WebWidget::LinkUrl> QtWebEngineWebWidget::getLinks() const
1768 {
1769 	return m_links;
1770 }
1771 
getSearchEngines() const1772 QVector<WebWidget::LinkUrl> QtWebEngineWebWidget::getSearchEngines() const
1773 {
1774 	return m_searchEngines;
1775 }
1776 
1777 #if QTWEBENGINECORE_VERSION >= 0x050D00
getBlockedRequests() const1778 QVector<NetworkManager::ResourceInformation> QtWebEngineWebWidget::getBlockedRequests() const
1779 {
1780 	return m_requestInterceptor->getBlockedRequests();
1781 }
1782 #endif
1783 
getMetaData() const1784 QMultiMap<QString, QString> QtWebEngineWebWidget::getMetaData() const
1785 {
1786 	return m_metaData;
1787 }
1788 
getLoadingState() const1789 WebWidget::LoadingState QtWebEngineWebWidget::getLoadingState() const
1790 {
1791 	return m_loadingState;
1792 }
1793 
getZoom() const1794 int QtWebEngineWebWidget::getZoom() const
1795 {
1796 	return static_cast<int>(m_page->zoomFactor() * 100);
1797 }
1798 
findInPage(const QString & text,FindFlags flags)1799 int QtWebEngineWebWidget::findInPage(const QString &text, FindFlags flags)
1800 {
1801 	if (text.isEmpty())
1802 	{
1803 		m_page->findText(text);
1804 
1805 		return 0;
1806 	}
1807 
1808 	QWebEnginePage::FindFlags nativeFlags(0);
1809 
1810 	if (flags.testFlag(BackwardFind))
1811 	{
1812 		nativeFlags |= QWebEnginePage::FindBackward;
1813 	}
1814 
1815 	if (flags.testFlag(CaseSensitiveFind))
1816 	{
1817 		nativeFlags |= QWebEnginePage::FindCaseSensitively;
1818 	}
1819 
1820 	QEventLoop eventLoop;
1821 	bool hasMatch(false);
1822 
1823 	m_page->findText(text, nativeFlags, [&](const QVariant &result)
1824 	{
1825 		hasMatch = result.toBool();
1826 
1827 		eventLoop.quit();
1828 	});
1829 
1830 	connect(this, &QtWebEngineWebWidget::aboutToReload, &eventLoop, &QEventLoop::quit);
1831 	connect(this, &QtWebEngineWebWidget::destroyed, &eventLoop, &QEventLoop::quit);
1832 
1833 	eventLoop.exec();
1834 
1835 	return (hasMatch ? -1 : 0);
1836 }
1837 
canGoBack() const1838 bool QtWebEngineWebWidget::canGoBack() const
1839 {
1840 	return m_page->history()->canGoBack();
1841 }
1842 
canGoForward() const1843 bool QtWebEngineWebWidget::canGoForward() const
1844 {
1845 	return m_page->history()->canGoForward();
1846 }
1847 
canFastForward() const1848 bool QtWebEngineWebWidget::canFastForward() const
1849 {
1850 	return (m_canGoForwardValue == TrueValue || canGoForward());
1851 }
1852 
1853 #if QTWEBENGINECORE_VERSION >= 0x050B00
canInspect() const1854 bool QtWebEngineWebWidget::canInspect() const
1855 {
1856 	return !Utils::isUrlEmpty(getUrl());
1857 }
1858 #endif
1859 
canRedo() const1860 bool QtWebEngineWebWidget::canRedo() const
1861 {
1862 	return m_page->action(QWebEnginePage::Redo)->isEnabled();
1863 }
1864 
canUndo() const1865 bool QtWebEngineWebWidget::canUndo() const
1866 {
1867 	return m_page->action(QWebEnginePage::Undo)->isEnabled();
1868 }
1869 
canShowContextMenu(const QPoint & position) const1870 bool QtWebEngineWebWidget::canShowContextMenu(const QPoint &position) const
1871 {
1872 	Q_UNUSED(position)
1873 
1874 	return true;
1875 }
1876 
canViewSource() const1877 bool QtWebEngineWebWidget::canViewSource() const
1878 {
1879 	return (!m_page->isViewingMedia() && !Utils::isUrlEmpty(getUrl()));
1880 }
1881 
hasSelection() const1882 bool QtWebEngineWebWidget::hasSelection() const
1883 {
1884 	return (m_page->hasSelection() && !m_page->selectedText().isEmpty());
1885 }
1886 
hasWatchedChanges(WebWidget::ChangeWatcher watcher) const1887 bool QtWebEngineWebWidget::hasWatchedChanges(WebWidget::ChangeWatcher watcher) const
1888 {
1889 	return m_watchedChanges.value(watcher, false);
1890 }
1891 
isAudible() const1892 bool QtWebEngineWebWidget::isAudible() const
1893 {
1894 	return m_page->recentlyAudible();
1895 }
1896 
isAudioMuted() const1897 bool QtWebEngineWebWidget::isAudioMuted() const
1898 {
1899 	return m_page->isAudioMuted();
1900 }
1901 
isFullScreen() const1902 bool QtWebEngineWebWidget::isFullScreen() const
1903 {
1904 	return m_isFullScreen;
1905 }
1906 
1907 #if QTWEBENGINECORE_VERSION >= 0x050B00
isInspecting() const1908 bool QtWebEngineWebWidget::isInspecting() const
1909 {
1910 	return (m_inspectorWidget && m_inspectorWidget->isVisible());
1911 }
1912 #endif
1913 
isPopup() const1914 bool QtWebEngineWebWidget::isPopup() const
1915 {
1916 	return m_page->isPopup();
1917 }
1918 
isPrivate() const1919 bool QtWebEngineWebWidget::isPrivate() const
1920 {
1921 	return m_page->profile()->isOffTheRecord();
1922 }
1923 
isScrollBar(const QPoint & position) const1924 bool QtWebEngineWebWidget::isScrollBar(const QPoint &position) const
1925 {
1926 	Q_UNUSED(position)
1927 
1928 	return false;
1929 }
1930 
isTypedIn() const1931 bool QtWebEngineWebWidget::isTypedIn() const
1932 {
1933 	return m_isTypedIn;
1934 }
1935 
eventFilter(QObject * object,QEvent * event)1936 bool QtWebEngineWebWidget::eventFilter(QObject *object, QEvent *event)
1937 {
1938 	switch (event->type())
1939 	{
1940 		case QEvent::ChildAdded:
1941 			if (object == m_webView)
1942 			{
1943 				const QChildEvent *childEvent(static_cast<QChildEvent*>(event));
1944 
1945 				if (childEvent->child())
1946 				{
1947 					childEvent->child()->installEventFilter(this);
1948 				}
1949 			}
1950 
1951 			break;
1952 		case QEvent::ContextMenu:
1953 			if (object == m_webView)
1954 			{
1955 				const QContextMenuEvent *contextMenuEvent(static_cast<QContextMenuEvent*>(event));
1956 
1957 				if (contextMenuEvent && contextMenuEvent->reason() != QContextMenuEvent::Mouse)
1958 				{
1959 					triggerAction(ActionsManager::ContextMenuAction, {{QLatin1String("context"), contextMenuEvent->reason()}});
1960 				}
1961 			}
1962 
1963 			break;
1964 		case QEvent::MouseButtonDblClick:
1965 		case QEvent::MouseButtonPress:
1966 		case QEvent::Wheel:
1967 			{
1968 				const QMouseEvent *mouseEvent(static_cast<QMouseEvent*>(event));
1969 
1970 				if (mouseEvent)
1971 				{
1972 					setClickPosition(mouseEvent->pos());
1973 					updateHitTestResult(mouseEvent->pos());
1974 
1975 					if (mouseEvent->button() == Qt::LeftButton && !getCurrentHitTestResult().linkUrl.isEmpty())
1976 					{
1977 						m_lastUrlClickTime = QDateTime::currentDateTime();
1978 					}
1979 				}
1980 
1981 				QVector<GesturesManager::GesturesContext> contexts;
1982 
1983 				if (getCurrentHitTestResult().flags.testFlag(HitTestResult::IsContentEditableTest))
1984 				{
1985 					contexts.append(GesturesManager::ContentEditableContext);
1986 				}
1987 
1988 				if (getCurrentHitTestResult().linkUrl.isValid())
1989 				{
1990 					contexts.append(GesturesManager::LinkContext);
1991 				}
1992 
1993 				contexts.append(GesturesManager::GenericContext);
1994 
1995 				if ((!mouseEvent || !isScrollBar(mouseEvent->pos())) && GesturesManager::startGesture(object, event, contexts))
1996 				{
1997 					return true;
1998 				}
1999 
2000 				if (event->type() == QEvent::MouseButtonDblClick && mouseEvent->button() == Qt::LeftButton && SettingsManager::getOption(SettingsManager::Browser_ShowSelectionContextMenuOnDoubleClickOption).toBool())
2001 				{
2002 					const HitTestResult hitResult(getHitTestResult(mouseEvent->pos()));
2003 
2004 					if (!hitResult.flags.testFlag(HitTestResult::IsContentEditableTest) && hitResult.tagName != QLatin1String("textarea") && hitResult.tagName!= QLatin1String("select") && hitResult.tagName != QLatin1String("input"))
2005 					{
2006 						setClickPosition(mouseEvent->pos());
2007 
2008 						QTimer::singleShot(250, this, [&]()
2009 						{
2010 							showContextMenu();
2011 						});
2012 					}
2013 				}
2014 			}
2015 
2016 			break;
2017 		case QEvent::Move:
2018 		case QEvent::Resize:
2019 			if (object == m_webView)
2020 			{
2021 				emit geometryChanged();
2022 			}
2023 
2024 			break;
2025 		case QEvent::ShortcutOverride:
2026 			m_isEditing = m_page->runScriptSource(QLatin1String("var element = document.body.querySelector(':focus'); var tagName = (element ? element.tagName.toLowerCase() : ''); var result = false; if (tagName == 'textarea' || tagName == 'input') { var type = (element.type ? element.type.toLowerCase() : ''); if ((type == '' || tagName == 'textarea' || type == 'text' || type == 'search') && !element.hasAttribute('readonly') && !element.hasAttribute('disabled')) { result = true; } } result;")).toBool();
2027 
2028 			if (m_isEditing)
2029 			{
2030 				event->accept();
2031 
2032 				return true;
2033 			}
2034 
2035 			break;
2036 		case QEvent::ToolTip:
2037 			handleToolTipEvent(static_cast<QHelpEvent*>(event), m_webView);
2038 
2039 			return true;
2040 		default:
2041 			break;
2042 	}
2043 
2044 	return QObject::eventFilter(object, event);
2045 }
2046 
2047 }
2048