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 Piotr Wójcik <chocimier@tlen.pl>
5 * Copyright (C) 2015 Jan Bajer aka bajasoft <jbajer@gmail.com>
6 * Copyright (C) 2017 Piktas Zuikis <piktas.zuikis@inbox.lt>
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 **************************************************************************/
22 
23 #include "WebWidget.h"
24 #include "ContentsDialog.h"
25 #include "ContentsWidget.h"
26 #include "Menu.h"
27 #include "TransferDialog.h"
28 #include "Window.h"
29 #include "../core/Application.h"
30 #include "../core/BookmarksManager.h"
31 #include "../core/ContentFiltersManager.h"
32 #include "../core/HandlersManager.h"
33 #include "../core/HistoryManager.h"
34 #include "../core/IniSettings.h"
35 #include "../core/SearchEnginesManager.h"
36 #include "../core/SettingsManager.h"
37 #include "../core/ThemesManager.h"
38 #include "../core/TransfersManager.h"
39 #include "../core/Utils.h"
40 
41 #include <QtCore/QDir>
42 #include <QtCore/QJsonArray>
43 #include <QtCore/QJsonDocument>
44 #include <QtGui/QClipboard>
45 #include <QtWidgets/QToolTip>
46 
47 namespace Otter
48 {
49 
50 QString WebWidget::m_fastForwardScript;
51 
WebWidget(const QVariantMap & parameters,WebBackend * backend,ContentsWidget * parent)52 WebWidget::WebWidget(const QVariantMap &parameters, WebBackend *backend, ContentsWidget *parent) : QWidget(parent), ActionExecutor(),
53 	m_parent(parent),
54 	m_backend(backend),
55 	m_windowIdentifier(0),
56 	m_loadingTime(0),
57 	m_loadingTimer(0),
58 	m_reloadTimer(0)
59 {
60 	Q_UNUSED(parameters)
61 
62 	connect(this, &WebWidget::loadingStateChanged, this, &WebWidget::handleLoadingStateChange);
63 	connect(BookmarksManager::getModel(), &BookmarksModel::modelModified, this, [&]()
64 	{
65 		emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::BookmarkCategory});
66 	});
67 	connect(PasswordsManager::getInstance(), &PasswordsManager::passwordsModified, this, [&]()
68 	{
69 		emit arbitraryActionsStateChanged({ActionsManager::FillPasswordAction});
70 	});
71 }
72 
timerEvent(QTimerEvent * event)73 void WebWidget::timerEvent(QTimerEvent *event)
74 {
75 	if (event->timerId() == m_loadingTimer)
76 	{
77 		++m_loadingTime;
78 
79 		emit pageInformationChanged(LoadingTimeInformation, m_loadingTime);
80 	}
81 	else if (event->timerId() == m_reloadTimer)
82 	{
83 		killTimer(m_reloadTimer);
84 
85 		m_reloadTimer = 0;
86 
87 		if (getLoadingState() == FinishedLoadingState)
88 		{
89 			triggerAction(ActionsManager::ReloadAction);
90 		}
91 	}
92 }
93 
triggerAction(int identifier,const QVariantMap & parameters,ActionsManager::TriggerType trigger)94 void WebWidget::triggerAction(int identifier, const QVariantMap &parameters, ActionsManager::TriggerType trigger)
95 {
96 	Q_UNUSED(identifier)
97 	Q_UNUSED(parameters)
98 	Q_UNUSED(trigger)
99 }
100 
search(const QString & query,const QString & searchEngine)101 void WebWidget::search(const QString &query, const QString &searchEngine)
102 {
103 	Q_UNUSED(query)
104 	Q_UNUSED(searchEngine)
105 }
106 
startWatchingChanges(QObject * object,ChangeWatcher watcher)107 void WebWidget::startWatchingChanges(QObject *object, ChangeWatcher watcher)
108 {
109 	if (!m_changeWatchers.contains(watcher))
110 	{
111 		m_changeWatchers[watcher] = {object};
112 
113 		updateWatchedData(watcher);
114 	}
115 	else if (!m_changeWatchers[watcher].contains(object))
116 	{
117 		m_changeWatchers[watcher].append(object);
118 	}
119 	else
120 	{
121 		return;
122 	}
123 
124 	connect(object, &QObject::destroyed, this, [=]()
125 	{
126 		stopWatchingChanges(object, watcher);
127 	});
128 }
129 
stopWatchingChanges(QObject * object,ChangeWatcher watcher)130 void WebWidget::stopWatchingChanges(QObject *object, ChangeWatcher watcher)
131 {
132 	if (m_changeWatchers.contains(watcher))
133 	{
134 		m_changeWatchers[watcher].removeAll(object);
135 	}
136 }
137 
startReloadTimer()138 void WebWidget::startReloadTimer()
139 {
140 	const int reloadTime(getOption(SettingsManager::Content_PageReloadTimeOption).toInt());
141 
142 	if (reloadTime >= 0)
143 	{
144 		triggerAction(ActionsManager::StopScheduledReloadAction);
145 
146 		if (reloadTime > 0)
147 		{
148 			m_reloadTimer = startTimer(reloadTime * 1000);
149 		}
150 	}
151 }
152 
startTransfer(Transfer * transfer)153 void WebWidget::startTransfer(Transfer *transfer)
154 {
155 	if (transfer->getState() == Transfer::CancelledState)
156 	{
157 		transfer->deleteLater();
158 
159 		return;
160 	}
161 
162 	const HandlersManager::HandlerDefinition handler(HandlersManager::getHandler(transfer->getMimeType()));
163 
164 	switch (handler.transferMode)
165 	{
166 		case HandlersManager::HandlerDefinition::IgnoreTransfer:
167 			transfer->cancel();
168 			transfer->deleteLater();
169 
170 			break;
171 		case HandlersManager::HandlerDefinition::AskTransfer:
172 			{
173 				TransferDialog *transferDialog(new TransferDialog(transfer, this));
174 				ContentsDialog *dialog(new ContentsDialog(ThemesManager::createIcon(QLatin1String("download")), transferDialog->windowTitle(), QString(), QString(), QDialogButtonBox::NoButton, transferDialog, this));
175 
176 				connect(transferDialog, &TransferDialog::finished, dialog, &ContentsDialog::close);
177 
178 				showDialog(dialog, false);
179 			}
180 
181 			break;
182 		case HandlersManager::HandlerDefinition::OpenTransfer:
183 			transfer->setOpenCommand(handler.openCommand);
184 
185 			TransfersManager::addTransfer(transfer);
186 
187 			break;
188 		case HandlersManager::HandlerDefinition::SaveTransfer:
189 			transfer->setTarget(handler.downloadsPath + QDir::separator() + transfer->getSuggestedFileName());
190 
191 			if (transfer->getState() == Transfer::CancelledState)
192 			{
193 				TransfersManager::addTransfer(transfer);
194 			}
195 			else
196 			{
197 				transfer->deleteLater();
198 			}
199 
200 			break;
201 		case HandlersManager::HandlerDefinition::SaveAsTransfer:
202 			{
203 				const QString path(Utils::getSavePath(transfer->getSuggestedFileName(), handler.downloadsPath, {}, true).path);
204 
205 				if (path.isEmpty())
206 				{
207 					transfer->cancel();
208 					transfer->deleteLater();
209 
210 					return;
211 				}
212 
213 				transfer->setTarget(path, true);
214 
215 				TransfersManager::addTransfer(transfer);
216 			}
217 
218 			break;
219 		default:
220 			break;
221 	}
222 }
223 
clearOptions()224 void WebWidget::clearOptions()
225 {
226 	const QString host(Utils::extractHost(getUrl()));
227 	const QList<int> identifiers(m_options.keys());
228 
229 	m_options.clear();
230 
231 	for (int i = 0; i < identifiers.count(); ++i)
232 	{
233 		emit optionChanged(identifiers.at(i), SettingsManager::getOption(identifiers.at(i), host));
234 	}
235 
236 	emit arbitraryActionsStateChanged({ActionsManager::ResetQuickPreferencesAction});
237 }
238 
fillPassword(const PasswordsManager::PasswordInformation & password)239 void WebWidget::fillPassword(const PasswordsManager::PasswordInformation &password)
240 {
241 	Q_UNUSED(password)
242 }
243 
openUrl(const QUrl & url,SessionsManager::OpenHints hints)244 void WebWidget::openUrl(const QUrl &url, SessionsManager::OpenHints hints)
245 {
246 	switch (hints)
247 	{
248 		case SessionsManager::CurrentTabOpen:
249 		case SessionsManager::DefaultOpen:
250 			setUrl(url, false);
251 
252 			break;
253 		default:
254 			{
255 				WebWidget *widget(clone(false, hints.testFlag(SessionsManager::PrivateOpen), SettingsManager::getOption(SettingsManager::Sessions_OptionsExludedFromInheritingOption).toStringList()));
256 				widget->setRequestedUrl(url, false);
257 
258 				emit requestedNewWindow(widget, hints, {});
259 			}
260 
261 			break;
262 	}
263 }
264 
handleLoadingStateChange(LoadingState state)265 void WebWidget::handleLoadingStateChange(LoadingState state)
266 {
267 	if (m_loadingTimer != 0)
268 	{
269 		killTimer(m_loadingTimer);
270 
271 		m_loadingTimer = 0;
272 	}
273 
274 	if (state == OngoingLoadingState)
275 	{
276 		m_loadingTime = 0;
277 		m_loadingTimer = startTimer(1000);
278 
279 		emit pageInformationChanged(LoadingTimeInformation, 0);
280 	}
281 }
282 
handleToolTipEvent(QHelpEvent * event,QWidget * widget)283 void WebWidget::handleToolTipEvent(QHelpEvent *event, QWidget *widget)
284 {
285 	const HitTestResult hitResult(getHitTestResult(event->pos()));
286 	const QString toolTipsMode(SettingsManager::getOption(SettingsManager::Browser_ToolTipsModeOption).toString());
287 	const QString link((hitResult.linkUrl.isValid() ? hitResult.linkUrl : hitResult.formUrl).toString());
288 	QString text;
289 
290 	if (toolTipsMode != QLatin1String("disabled"))
291 	{
292 		const QString title(QString(hitResult.title).replace(QLatin1Char('&'), QLatin1String("&amp;")).replace(QLatin1Char('<'), QLatin1String("&lt;")).replace(QLatin1Char('>'), QLatin1String("&gt;")));
293 
294 		if (toolTipsMode == QLatin1String("extended"))
295 		{
296 			if (!link.isEmpty())
297 			{
298 				text = (title.isEmpty() ? QString() : tr("Title: %1").arg(title) + QLatin1String("<br>")) + tr("Address: %1").arg(link);
299 			}
300 			else if (!title.isEmpty())
301 			{
302 				text = title;
303 			}
304 		}
305 		else
306 		{
307 			text = title;
308 		}
309 	}
310 
311 	setStatusMessageOverride(link.isEmpty() ? hitResult.title : link);
312 
313 	if (!text.isEmpty())
314 	{
315 		QToolTip::showText(event->globalPos(), QStringLiteral("<div style=\"white-space:pre-line;\">%1</div>").arg(text), widget);
316 	}
317 
318 	event->accept();
319 }
320 
handleWindowCloseRequest()321 void WebWidget::handleWindowCloseRequest()
322 {
323 	const QString host(Utils::extractHost(getUrl()));
324 
325 	if (isPopup() && SettingsManager::getOption(SettingsManager::Permissions_ScriptsCanCloseSelfOpenedWindowsOption, host).toBool())
326 	{
327 		emit requestedCloseWindow();
328 
329 		return;
330 	}
331 
332 	const QString mode(SettingsManager::getOption(SettingsManager::Permissions_ScriptsCanCloseWindowsOption, host).toString());
333 
334 	if (mode != QLatin1String("ask"))
335 	{
336 		if (mode == QLatin1String("allow"))
337 		{
338 			emit requestedCloseWindow();
339 		}
340 
341 		return;
342 	}
343 
344 	ContentsDialog *dialog(new ContentsDialog(ThemesManager::createIcon(QLatin1String("dialog-warning")), tr("JavaScript"), tr("Webpage wants to close this tab, do you want to allow to close it?"), QString(), (QDialogButtonBox::Ok | QDialogButtonBox::Cancel), nullptr, this));
345 	dialog->setCheckBox(tr("Do not show this message again"), false);
346 
347 	connect(this, &WebWidget::aboutToReload, dialog, &ContentsDialog::close);
348 	connect(dialog, &ContentsDialog::finished, [&](int result, bool isChecked)
349 	{
350 		const bool isAccepted(result == QDialog::Accepted);
351 
352 		if (isChecked)
353 		{
354 			SettingsManager::setOption(SettingsManager::Permissions_ScriptsCanCloseWindowsOption, (isAccepted ? QLatin1String("allow") : QLatin1String("disallow")));
355 		}
356 
357 		if (isAccepted)
358 		{
359 			emit requestedCloseWindow();
360 		}
361 	});
362 
363 	showDialog(dialog, false);
364 }
365 
notifyRedoActionStateChanged()366 void WebWidget::notifyRedoActionStateChanged()
367 {
368 	emit arbitraryActionsStateChanged({ActionsManager::RedoAction});
369 }
370 
notifyUndoActionStateChanged()371 void WebWidget::notifyUndoActionStateChanged()
372 {
373 	emit arbitraryActionsStateChanged({ActionsManager::UndoAction});
374 }
375 
updateHitTestResult(const QPoint & position)376 void WebWidget::updateHitTestResult(const QPoint &position)
377 {
378 	m_hitResult = getHitTestResult(position);
379 }
380 
updateWatchedData(ChangeWatcher watcher)381 void WebWidget::updateWatchedData(ChangeWatcher watcher)
382 {
383 	Q_UNUSED(watcher)
384 }
385 
showDialog(ContentsDialog * dialog,bool lockEventLoop)386 void WebWidget::showDialog(ContentsDialog *dialog, bool lockEventLoop)
387 {
388 	if (m_parent)
389 	{
390 		m_parent->showDialog(dialog, lockEventLoop);
391 	}
392 }
393 
showContextMenu(const QPoint & position)394 void WebWidget::showContextMenu(const QPoint &position)
395 {
396 	const bool hasSelection(this->hasSelection() && !getSelectedText().trimmed().isEmpty());
397 
398 	if (position.isNull() && (!hasSelection || m_clickPosition.isNull()))
399 	{
400 		return;
401 	}
402 
403 	const QPoint hitPosition(position.isNull() ? m_clickPosition : position);
404 
405 	if (isScrollBar(hitPosition) || !canShowContextMenu(hitPosition))
406 	{
407 		return;
408 	}
409 
410 	updateHitTestResult(hitPosition);
411 
412 	emit categorizedActionsStateChanged({ActionsManager::ActionDefinition::EditingCategory});
413 
414 	QStringList includeSections;
415 
416 	if (m_hitResult.flags.testFlag(HitTestResult::IsFormTest))
417 	{
418 		includeSections.append(QLatin1String("form"));
419 	}
420 
421 	if (!m_hitResult.imageUrl.isValid() && m_hitResult.flags.testFlag(HitTestResult::IsSelectedTest) && hasSelection)
422 	{
423 		includeSections.append(QLatin1String("selection"));
424 	}
425 
426 	if (m_hitResult.linkUrl.isValid())
427 	{
428 		if (m_hitResult.linkUrl.scheme() == QLatin1String("mailto"))
429 		{
430 			includeSections.append(QLatin1String("mail"));
431 		}
432 		else
433 		{
434 			includeSections.append(QLatin1String("link"));
435 		}
436 	}
437 
438 	if (!m_hitResult.imageUrl.isEmpty())
439 	{
440 		includeSections.append(QLatin1String("image"));
441 	}
442 
443 	if (m_hitResult.mediaUrl.isValid())
444 	{
445 		includeSections.append(QLatin1String("media"));
446 	}
447 
448 	if (m_hitResult.flags.testFlag(HitTestResult::IsContentEditableTest))
449 	{
450 		includeSections.append(QLatin1String("edit"));
451 	}
452 
453 	if (includeSections.isEmpty() || (includeSections.count() == 1 && includeSections.first() == QLatin1String("form")))
454 	{
455 		includeSections.append(QLatin1String("standard"));
456 
457 		if (m_hitResult.frameUrl.isValid())
458 		{
459 			includeSections.append(QLatin1String("frame"));
460 		}
461 	}
462 
463 	if (includeSections.isEmpty())
464 	{
465 		return;
466 	}
467 
468 	ActionExecutor::Object executor;
469 
470 	if (m_parent)
471 	{
472 		if (m_parent->getWindow())
473 		{
474 			executor = ActionExecutor::Object(m_parent->getWindow(), m_parent->getWindow());
475 		}
476 		else
477 		{
478 			executor = ActionExecutor::Object(m_parent, m_parent);
479 		}
480 	}
481 	else
482 	{
483 		executor = ActionExecutor::Object(this, this);
484 	}
485 
486 	Menu menu(Menu::UnknownMenu, this);
487 	menu.load(QLatin1String("menu/webWidget.json"), includeSections, executor);
488 	menu.exec(mapToGlobal(hitPosition));
489 }
490 
setParent(QWidget * parent)491 void WebWidget::setParent(QWidget *parent)
492 {
493 	QWidget::setParent(parent);
494 
495 	ContentsWidget *contentsWidget(qobject_cast<ContentsWidget*>(parent));
496 
497 	if (contentsWidget)
498 	{
499 		m_parent = contentsWidget;
500 	}
501 }
502 
setActiveStyleSheet(const QString & styleSheet)503 void WebWidget::setActiveStyleSheet(const QString &styleSheet)
504 {
505 	Q_UNUSED(styleSheet)
506 }
507 
setClickPosition(const QPoint & position)508 void WebWidget::setClickPosition(const QPoint &position)
509 {
510 	m_clickPosition = position;
511 }
512 
setStatusMessage(const QString & message)513 void WebWidget::setStatusMessage(const QString &message)
514 {
515 	const QString previousMessage(getStatusMessage());
516 
517 	m_statusMessage = message;
518 
519 	const QString currentMessage(getStatusMessage());
520 
521 	if (currentMessage != previousMessage)
522 	{
523 		emit statusMessageChanged(currentMessage);
524 	}
525 }
526 
setStatusMessageOverride(const QString & message)527 void WebWidget::setStatusMessageOverride(const QString &message)
528 {
529 	const QString previousMessage(getStatusMessage());
530 
531 	m_statusMessageOverride = message;
532 
533 	const QString currentMessage(getStatusMessage());
534 
535 	if (currentMessage != previousMessage)
536 	{
537 		emit statusMessageChanged(currentMessage);
538 	}
539 }
540 
setPermission(FeaturePermission feature,const QUrl & url,PermissionPolicies policies)541 void WebWidget::setPermission(FeaturePermission feature, const QUrl &url, PermissionPolicies policies)
542 {
543 	if (policies.testFlag(KeepAskingPermission))
544 	{
545 		return;
546 	}
547 
548 	const QString value(policies.testFlag(GrantedPermission) ? QLatin1String("allow") : QLatin1String("disallow"));
549 	const QString host(Utils::extractHost(url));
550 
551 	switch (feature)
552 	{
553 		case FullScreenFeature:
554 			SettingsManager::setOption(SettingsManager::Permissions_EnableFullScreenOption, value, host);
555 
556 			return;
557 		case GeolocationFeature:
558 			SettingsManager::setOption(SettingsManager::Permissions_EnableGeolocationOption, value, host);
559 
560 			return;
561 		case NotificationsFeature:
562 			SettingsManager::setOption(SettingsManager::Permissions_EnableNotificationsOption, value, host);
563 
564 			return;
565 		case PointerLockFeature:
566 			SettingsManager::setOption(SettingsManager::Permissions_EnablePointerLockOption, value, host);
567 
568 			return;
569 		case CaptureAudioFeature:
570 			SettingsManager::setOption(SettingsManager::Permissions_EnableMediaCaptureAudioOption, value, host);
571 
572 			return;
573 		case CaptureVideoFeature:
574 			SettingsManager::setOption(SettingsManager::Permissions_EnableMediaCaptureVideoOption, value, host);
575 
576 			return;
577 		case CaptureAudioVideoFeature:
578 			SettingsManager::setOption(SettingsManager::Permissions_EnableMediaCaptureAudioOption, value, host);
579 			SettingsManager::setOption(SettingsManager::Permissions_EnableMediaCaptureVideoOption, value, host);
580 
581 			return;
582 		case PlaybackAudioFeature:
583 			SettingsManager::setOption(SettingsManager::Permissions_EnableMediaPlaybackAudioOption, value, host);
584 
585 			return;
586 		default:
587 			return;
588 	}
589 }
590 
setOption(int identifier,const QVariant & value)591 void WebWidget::setOption(int identifier, const QVariant &value)
592 {
593 	if (value == m_options.value(identifier))
594 	{
595 		return;
596 	}
597 
598 	if (value.isNull())
599 	{
600 		m_options.remove(identifier);
601 	}
602 	else
603 	{
604 		m_options[identifier] = value;
605 	}
606 
607 	SessionsManager::markSessionAsModified();
608 
609 	switch (identifier)
610 	{
611 		case SettingsManager::Content_PageReloadTimeOption:
612 			{
613 				const int reloadTime(value.toInt());
614 
615 				emit arbitraryActionsStateChanged({ActionsManager::ResetQuickPreferencesAction});
616 				emit optionChanged(identifier, (value.isNull() ? getOption(identifier) : value));
617 
618 				if (m_reloadTimer != 0)
619 				{
620 					killTimer(m_reloadTimer);
621 
622 					m_reloadTimer = 0;
623 				}
624 
625 				if (reloadTime >= 0)
626 				{
627 					triggerAction(ActionsManager::StopScheduledReloadAction);
628 
629 					if (reloadTime > 0)
630 					{
631 						m_reloadTimer = startTimer(reloadTime * 1000);
632 					}
633 				}
634 			}
635 
636 			break;
637 		case SettingsManager::Network_EnableReferrerOption:
638 			emit arbitraryActionsStateChanged({ActionsManager::EnableReferrerAction});
639 
640 			break;
641 		case SettingsManager::Permissions_EnableJavaScriptOption:
642 			emit arbitraryActionsStateChanged({ActionsManager::EnableJavaScriptAction});
643 
644 			break;
645 		default:
646 			break;
647 	}
648 }
649 
setOptions(const QHash<int,QVariant> & options,const QStringList & excludedOptions)650 void WebWidget::setOptions(const QHash<int, QVariant> &options, const QStringList &excludedOptions)
651 {
652 	const QList<int> identifiers((m_options.keys() + options.keys()).toSet().toList());
653 
654 	m_options = options;
655 
656 	for (int i = 0; i < excludedOptions.count(); ++i)
657 	{
658 		const int identifier(SettingsManager::getOptionIdentifier(excludedOptions.at(i)));
659 
660 		if (identifier >= 0 && m_options.contains(identifier))
661 		{
662 			m_options.remove(identifier);
663 		}
664 	}
665 
666 	const QString host(Utils::extractHost(getUrl()));
667 
668 	for (int i = 0; i < identifiers.count(); ++i)
669 	{
670 		if (m_options.contains(identifiers.at(i)))
671 		{
672 			emit optionChanged(identifiers.at(i), m_options[identifiers.at(i)]);
673 		}
674 		else
675 		{
676 			emit optionChanged(identifiers.at(i), SettingsManager::getOption(identifiers.at(i), host));
677 		}
678 	}
679 
680 	emit arbitraryActionsStateChanged({ActionsManager::ResetQuickPreferencesAction});
681 }
682 
setRequestedUrl(const QUrl & url,bool isTyped,bool onlyUpdate)683 void WebWidget::setRequestedUrl(const QUrl &url, bool isTyped, bool onlyUpdate)
684 {
685 	m_requestedUrl = url;
686 
687 	if (onlyUpdate)
688 	{
689 		emit urlChanged(url);
690 	}
691 	else
692 	{
693 		setUrl(url, isTyped);
694 	}
695 }
696 
setWindowIdentifier(quint64 identifier)697 void WebWidget::setWindowIdentifier(quint64 identifier)
698 {
699 	m_windowIdentifier = identifier;
700 }
701 
getInspector()702 QWidget* WebWidget::getInspector()
703 {
704 	return nullptr;
705 }
706 
getViewport()707 QWidget* WebWidget::getViewport()
708 {
709 	return this;
710 }
711 
getBackend() const712 WebBackend* WebWidget::getBackend() const
713 {
714 	return m_backend;
715 }
716 
getDescription() const717 QString WebWidget::getDescription() const
718 {
719 	return {};
720 }
721 
suggestSaveFileName(const QString & extension) const722 QString WebWidget::suggestSaveFileName(const QString &extension) const
723 {
724 	const QUrl url(getUrl());
725 	QString fileName(url.fileName());
726 
727 	if (fileName.isEmpty() && !url.path().isEmpty() && url.path() != QLatin1String("/"))
728 	{
729 		fileName = QDir(url.path()).dirName();
730 	}
731 
732 	if (fileName.isEmpty() && !url.host().isEmpty())
733 	{
734 		fileName = url.host() + extension;
735 	}
736 
737 	if (fileName.isEmpty())
738 	{
739 		fileName = QLatin1String("file") + extension;
740 	}
741 
742 	if (!fileName.contains(QLatin1Char('.')) && !extension.isEmpty())
743 	{
744 		fileName.append(extension);
745 	}
746 
747 	return fileName;
748 }
749 
suggestSaveFileName(SaveFormat format) const750 QString WebWidget::suggestSaveFileName(SaveFormat format) const
751 {
752 	switch (format)
753 	{
754 		case MhtmlSaveFormat:
755 			return suggestSaveFileName(QLatin1String(".mht"));
756 		case PdfSaveFormat:
757 			return suggestSaveFileName(QLatin1String(".pdf"));
758 		case SingleFileSaveFormat:
759 			return suggestSaveFileName(QLatin1String(".html"));
760 		default:
761 			break;
762 	}
763 
764 	return suggestSaveFileName({});
765 }
766 
getSavePath(const QVector<SaveFormat> & allowedFormats,SaveFormat * selectedFormat) const767 QString WebWidget::getSavePath(const QVector<SaveFormat> &allowedFormats, SaveFormat *selectedFormat) const
768 {
769 	const QMap<SaveFormat, QString> formats({{SingleFileSaveFormat, tr("HTML file (*.html *.htm)")}, {CompletePageSaveFormat, tr("HTML file with all resources (*.html *.htm)")}, {MhtmlSaveFormat, tr("Web archive (*.mht)")}, {PdfSaveFormat, tr("PDF document (*.pdf)")}});
770 	QStringList filters;
771 	filters.reserve(allowedFormats.count());
772 
773 	for (int i = 0; i < allowedFormats.count(); ++i)
774 	{
775 		filters.append(formats.value(allowedFormats.at(i)));
776 	}
777 
778 	const SaveInformation result(Utils::getSavePath(suggestSaveFileName(SingleFileSaveFormat), {}, filters));
779 
780 	if (!result.canSave)
781 	{
782 		return {};
783 	}
784 
785 	*selectedFormat = formats.key(result.filter);
786 
787 	return result.path;
788 }
789 
getOpenActionText(SessionsManager::OpenHints hints) const790 QString WebWidget::getOpenActionText(SessionsManager::OpenHints hints) const
791 {
792 	if (hints == SessionsManager::CurrentTabOpen)
793 	{
794 		return QCoreApplication::translate("actions", "Open in This Tab");
795 	}
796 
797 	if (hints == SessionsManager::NewTabOpen)
798 	{
799 		return QCoreApplication::translate("actions", "Open in New Tab");
800 	}
801 
802 	if (hints == (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen))
803 	{
804 		return QCoreApplication::translate("actions", "Open in New Background Tab");
805 	}
806 
807 	if (hints == SessionsManager::NewWindowOpen)
808 	{
809 		return QCoreApplication::translate("actions", "Open in New Window");
810 	}
811 
812 	if (hints == (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen))
813 	{
814 		return QCoreApplication::translate("actions", "Open in New Background Window");
815 	}
816 
817 	if (hints == (SessionsManager::NewTabOpen | SessionsManager::PrivateOpen))
818 	{
819 		return QCoreApplication::translate("actions", "Open in New Private Tab");
820 	}
821 
822 	if (hints == (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen))
823 	{
824 		return QCoreApplication::translate("actions", "Open in New Private Background Tab");
825 	}
826 
827 	if (hints == (SessionsManager::NewWindowOpen | SessionsManager::PrivateOpen))
828 	{
829 		return QCoreApplication::translate("actions", "Open in New Private Window");
830 	}
831 
832 	if (hints == (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen))
833 	{
834 		return QCoreApplication::translate("actions", "Open in New Private Background Window");
835 	}
836 
837 	return QCoreApplication::translate("actions", "Open");
838 }
839 
getFastForwardScript(bool isSelectingTheBestLink)840 QString WebWidget::getFastForwardScript(bool isSelectingTheBestLink)
841 {
842 	QString script(m_fastForwardScript);
843 
844 	if (m_fastForwardScript.isEmpty())
845 	{
846 		IniSettings settings(SessionsManager::getReadableDataPath(QLatin1String("fastforward.ini")));
847 		QFile file(SessionsManager::getReadableDataPath(QLatin1String("fastforward.js")));
848 
849 		if (!file.open(QIODevice::ReadOnly))
850 		{
851 			return {};
852 		}
853 
854 		script = file.readAll();
855 
856 		file.close();
857 
858 		const QStringList categories({QLatin1String("Href"), QLatin1String("Class"), QLatin1String("Id"), QLatin1String("Text")});
859 
860 		for (int i = 0; i < categories.count(); ++i)
861 		{
862 			settings.beginGroup(categories.at(i));
863 
864 			const QStringList keys(settings.getKeys());
865 			QJsonArray tokensArray;
866 
867 			for (int j = 0; j < keys.count(); ++j)
868 			{
869 				tokensArray.append(QJsonObject({{QLatin1Literal("value"), keys.at(j).toUpper()}, {QLatin1Literal("score"), settings.getValue(keys.at(j)).toInt()}}));
870 			}
871 
872 			settings.endGroup();
873 
874 			script.replace(QLatin1Char('{') + categories.at(i).toLower() + QLatin1String("Tokens}"), QString::fromUtf8(QJsonDocument(tokensArray).toJson(QJsonDocument::Compact)));
875 		}
876 
877 		m_fastForwardScript = script;
878 	}
879 
880 	return script.replace(QLatin1String("{isSelectingTheBestLink}"), (isSelectingTheBestLink ? QLatin1String("true") : QLatin1String("false")));
881 }
882 
getActiveStyleSheet() const883 QString WebWidget::getActiveStyleSheet() const
884 {
885 	return {};
886 }
887 
getCharacterEncoding() const888 QString WebWidget::getCharacterEncoding() const
889 {
890 	return {};
891 }
892 
getSelectedText() const893 QString WebWidget::getSelectedText() const
894 {
895 	return {};
896 }
897 
getStatusMessage() const898 QString WebWidget::getStatusMessage() const
899 {
900 	return (m_statusMessageOverride.isEmpty() ? m_statusMessage : m_statusMessageOverride);
901 }
902 
getOption(int identifier,const QUrl & url) const903 QVariant WebWidget::getOption(int identifier, const QUrl &url) const
904 {
905 	if (m_options.contains(identifier))
906 	{
907 		return m_options[identifier];
908 	}
909 
910 	return SettingsManager::getOption(identifier, Utils::extractHost(url.isEmpty() ? getUrl() : url));
911 }
912 
getPageInformation(PageInformation key) const913 QVariant WebWidget::getPageInformation(PageInformation key) const
914 {
915 	if (key == LoadingTimeInformation)
916 	{
917 		return m_loadingTime;
918 	}
919 
920 	return {};
921 }
922 
getRequestedUrl() const923 QUrl WebWidget::getRequestedUrl() const
924 {
925 	return ((getUrl().isEmpty() || getLoadingState() == OngoingLoadingState) ? m_requestedUrl : getUrl());
926 }
927 
createThumbnail(const QSize & size)928 QPixmap WebWidget::createThumbnail(const QSize &size)
929 {
930 	Q_UNUSED(size)
931 
932 	return {};
933 }
934 
getClickPosition() const935 QPoint WebWidget::getClickPosition() const
936 {
937 	return m_clickPosition;
938 }
939 
getGeometry(bool excludeScrollBars) const940 QRect WebWidget::getGeometry(bool excludeScrollBars) const
941 {
942 	Q_UNUSED(excludeScrollBars)
943 
944 	return geometry();
945 }
946 
getActionState(int identifier,const QVariantMap & parameters) const947 ActionsManager::ActionDefinition::State WebWidget::getActionState(int identifier, const QVariantMap &parameters) const
948 {
949 	ActionsManager::ActionDefinition::State state(ActionsManager::getActionDefinition(identifier).getDefaultState());
950 
951 	switch (identifier)
952 	{
953 		case ActionsManager::ClearTabHistoryAction:
954 			state.isEnabled = !getHistory().isEmpty();
955 
956 			if (parameters.value(QLatin1String("clearGlobalHistory"), false).toBool())
957 			{
958 				state.text = QCoreApplication::translate("actions", "Purge Tab History");
959 			}
960 
961 			break;
962 		case ActionsManager::PurgeTabHistoryAction:
963 			state.isEnabled = !getHistory().isEmpty();
964 
965 			break;
966 		case ActionsManager::OpenLinkAction:
967 		case ActionsManager::OpenLinkInCurrentTabAction:
968 		case ActionsManager::OpenLinkInNewTabAction:
969 		case ActionsManager::OpenLinkInNewTabBackgroundAction:
970 		case ActionsManager::OpenLinkInNewWindowAction:
971 		case ActionsManager::OpenLinkInNewWindowBackgroundAction:
972 		case ActionsManager::OpenLinkInNewPrivateTabAction:
973 		case ActionsManager::OpenLinkInNewPrivateTabBackgroundAction:
974 		case ActionsManager::OpenLinkInNewPrivateWindowAction:
975 		case ActionsManager::OpenLinkInNewPrivateWindowBackgroundAction:
976 		case ActionsManager::CopyLinkToClipboardAction:
977 		case ActionsManager::SaveLinkToDiskAction:
978 		case ActionsManager::SaveLinkToDownloadsAction:
979 			state.isEnabled = m_hitResult.linkUrl.isValid();
980 
981 			if (identifier == ActionsManager::OpenLinkAction && parameters.contains(QLatin1String("hints")))
982 			{
983 				const SessionsManager::OpenHints hints(SessionsManager::calculateOpenHints(parameters));
984 
985 				state.text = getOpenActionText(hints);
986 
987 				if (hints != SessionsManager::DefaultOpen)
988 				{
989 					state.icon = {};
990 				}
991 			}
992 
993 			break;
994 		case ActionsManager::BookmarkLinkAction:
995 			state.text = (BookmarksManager::hasBookmark(m_hitResult.linkUrl) ? QCoreApplication::translate("actions", "Edit Link Bookmark…") : QCoreApplication::translate("actions", "Bookmark Link…"));
996 			state.isEnabled = m_hitResult.linkUrl.isValid();
997 
998 			break;
999 		case ActionsManager::OpenFrameAction:
1000 		case ActionsManager::OpenFrameInCurrentTabAction:
1001 		case ActionsManager::OpenFrameInNewTabAction:
1002 		case ActionsManager::OpenFrameInNewTabBackgroundAction:
1003 		case ActionsManager::CopyFrameLinkToClipboardAction:
1004 		case ActionsManager::ReloadFrameAction:
1005 		case ActionsManager::ViewFrameSourceAction:
1006 			state.isEnabled = m_hitResult.frameUrl.isValid();
1007 
1008 			if (identifier == ActionsManager::OpenFrameAction && parameters.contains(QLatin1String("hints")))
1009 			{
1010 				state.text = getOpenActionText(SessionsManager::calculateOpenHints(parameters));
1011 			}
1012 
1013 			break;
1014 		case ActionsManager::OpenImageAction:
1015 		case ActionsManager::OpenImageInNewTabAction:
1016 		case ActionsManager::OpenImageInNewTabBackgroundAction:
1017 			if (m_hitResult.imageUrl.isValid())
1018 			{
1019 				const QString fileName((m_hitResult.imageUrl.scheme() == QLatin1String("data")) ? QString() : fontMetrics().elidedText(m_hitResult.imageUrl.fileName(), Qt::ElideMiddle, 256));
1020 
1021 				if (identifier == ActionsManager::OpenImageAction)
1022 				{
1023 					const SessionsManager::OpenHints hints(SessionsManager::calculateOpenHints(parameters));
1024 
1025 					if (hints == SessionsManager::CurrentTabOpen)
1026 					{
1027 						state.text = QCoreApplication::translate("actions", "Open Image in This Tab");
1028 					}
1029 					else if (hints == SessionsManager::NewTabOpen)
1030 					{
1031 						state.text = QCoreApplication::translate("actions", "Open Image in New Tab");
1032 					}
1033 					else if (hints == (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen))
1034 					{
1035 						state.text = QCoreApplication::translate("actions", "Open Image in New Background Tab");
1036 					}
1037 					else if (hints == SessionsManager::NewWindowOpen)
1038 					{
1039 						state.text = QCoreApplication::translate("actions", "Open Image in New Window");
1040 					}
1041 					else if (hints == (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen))
1042 					{
1043 						state.text = QCoreApplication::translate("actions", "Open Image in New Background Window");
1044 					}
1045 					else if (hints == (SessionsManager::NewTabOpen | SessionsManager::PrivateOpen))
1046 					{
1047 						state.text = QCoreApplication::translate("actions", "Open Image in New Private Tab");
1048 					}
1049 					else if (hints == (SessionsManager::NewTabOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen))
1050 					{
1051 						state.text = QCoreApplication::translate("actions", "Open Image in New Private Background Tab");
1052 					}
1053 					else if (hints == (SessionsManager::NewWindowOpen | SessionsManager::PrivateOpen))
1054 					{
1055 						state.text = QCoreApplication::translate("actions", "Open Image in New Private Window");
1056 					}
1057 					else if (hints == (SessionsManager::NewWindowOpen | SessionsManager::BackgroundOpen | SessionsManager::PrivateOpen))
1058 					{
1059 						state.text = QCoreApplication::translate("actions", "Open Image in New Private Background Window");
1060 					}
1061 
1062 					if (!fileName.isEmpty())
1063 					{
1064 						state.text.append(QLatin1String(" (") + fileName + QLatin1Char(')'));
1065 					}
1066 				}
1067 				else if (!fileName.isEmpty())
1068 				{
1069 					if (identifier == ActionsManager::OpenImageInNewTabBackgroundAction)
1070 					{
1071 						state.text = tr("Open Image in New Background Tab (%1)").arg(fileName);
1072 					}
1073 					else
1074 					{
1075 						state.text = tr("Open Image in New Tab (%1)").arg(fileName);
1076 					}
1077 				}
1078 
1079 				state.isEnabled = !getUrl().matches(m_hitResult.imageUrl, (QUrl::NormalizePathSegments | QUrl::RemoveFragment | QUrl::StripTrailingSlash));
1080 			}
1081 			else
1082 			{
1083 				state.isEnabled = false;
1084 			}
1085 
1086 			break;
1087 		case ActionsManager::SaveImageToDiskAction:
1088 		case ActionsManager::CopyImageToClipboardAction:
1089 		case ActionsManager::CopyImageUrlToClipboardAction:
1090 		case ActionsManager::ReloadImageAction:
1091 		case ActionsManager::ImagePropertiesAction:
1092 			state.isEnabled = m_hitResult.imageUrl.isValid();
1093 
1094 			break;
1095 		case ActionsManager::SaveMediaToDiskAction:
1096 			state.text = ((m_hitResult.tagName == QLatin1String("video")) ? QCoreApplication::translate("actions", "Save Video…") : QCoreApplication::translate("actions", "Save Audio…"));
1097 			state.isEnabled = m_hitResult.mediaUrl.isValid();
1098 
1099 			break;
1100 		case ActionsManager::CopyMediaUrlToClipboardAction:
1101 			state.text = ((m_hitResult.tagName == QLatin1String("video")) ? QCoreApplication::translate("actions", "Copy Video Link to Clipboard") : QCoreApplication::translate("actions", "Copy Audio Link to Clipboard"));
1102 			state.isEnabled = m_hitResult.mediaUrl.isValid();
1103 
1104 			break;
1105 		case ActionsManager::MediaControlsAction:
1106 			state.isChecked = m_hitResult.flags.testFlag(HitTestResult::MediaHasControlsTest);
1107 			state.isEnabled = m_hitResult.mediaUrl.isValid();
1108 
1109 			break;
1110 		case ActionsManager::MediaLoopAction:
1111 			state.isChecked = m_hitResult.flags.testFlag(HitTestResult::MediaIsLoopedTest);
1112 			state.isEnabled = m_hitResult.mediaUrl.isValid();
1113 
1114 			break;
1115 		case ActionsManager::MediaPlayPauseAction:
1116 			state.text = (m_hitResult.flags.testFlag(HitTestResult::MediaIsPausedTest) ? QCoreApplication::translate("actions", "Play") : QCoreApplication::translate("actions", "Pause"));
1117 			state.icon = ThemesManager::createIcon(m_hitResult.flags.testFlag(HitTestResult::MediaIsPausedTest) ? QLatin1String("media-playback-start") : QLatin1String("media-playback-pause"));
1118 			state.isEnabled = m_hitResult.mediaUrl.isValid();
1119 
1120 			break;
1121 		case ActionsManager::MediaMuteAction:
1122 			state.text = (m_hitResult.flags.testFlag(HitTestResult::MediaIsMutedTest) ? QCoreApplication::translate("actions", "Unmute") : QCoreApplication::translate("actions", "Mute"));
1123 			state.icon = ThemesManager::createIcon(m_hitResult.flags.testFlag(HitTestResult::MediaIsMutedTest) ? QLatin1String("audio-volume-medium") : QLatin1String("audio-volume-muted"));
1124 			state.isEnabled = m_hitResult.mediaUrl.isValid();
1125 
1126 			break;
1127 		case ActionsManager::MediaPlaybackRateAction:
1128 			{
1129 				const qreal rate(parameters.value(QLatin1String("rate")).toReal());
1130 
1131 				state.text = tr("Playback Rate: %1x").arg(QLocale().toString(rate));
1132 				state.isChecked = qFuzzyCompare(rate, m_hitResult.playbackRate);
1133 				state.isEnabled = m_hitResult.mediaUrl.isValid();
1134 			}
1135 
1136 			break;
1137 		case ActionsManager::FillPasswordAction:
1138 			state.isEnabled = (!Utils::isUrlEmpty(getUrl()) && PasswordsManager::hasPasswords(getUrl(), PasswordsManager::FormPassword));
1139 
1140 			break;
1141 		case ActionsManager::MuteTabMediaAction:
1142 			state.icon = ThemesManager::createIcon(isAudioMuted() ? QLatin1String("audio-volume-muted") : QLatin1String("audio-volume-medium"));
1143 			state.text = (isAudioMuted() ? QCoreApplication::translate("actions", "Unmute Tab Media") : QCoreApplication::translate("actions", "Mute Tab Media"));
1144 
1145 			break;
1146 		case ActionsManager::GoBackAction:
1147 		case ActionsManager::RewindAction:
1148 			state.isEnabled = canGoBack();
1149 
1150 			break;
1151 		case ActionsManager::GoForwardAction:
1152 			state.isEnabled = canGoForward();
1153 
1154 			break;
1155 		case ActionsManager::GoToHistoryIndexAction:
1156 			if (parameters.contains(QLatin1String("index")))
1157 			{
1158 				const WindowHistoryInformation history(getHistory());
1159 				const int index(parameters[QLatin1String("index")].toInt());
1160 
1161 				if (index >= 0 && index < history.entries.count())
1162 				{
1163 					state.icon = HistoryManager::getIcon(QUrl(history.entries.at(index).url));
1164 					state.text = history.entries.at(index).getTitle().replace(QLatin1Char('&'), QLatin1String("&&"));
1165 					state.isEnabled = true;
1166 				}
1167 			}
1168 
1169 			break;
1170 		case ActionsManager::FastForwardAction:
1171 			state.isEnabled = canFastForward();
1172 
1173 			break;
1174 		case ActionsManager::RemoveHistoryIndexAction:
1175 			if (parameters.value(QLatin1String("clearGlobalHistory"), false).toBool())
1176 			{
1177 				state.text = QCoreApplication::translate("actions", "Purge History Entry");
1178 			}
1179 
1180 			if (parameters.contains(QLatin1String("index")))
1181 			{
1182 				const int index(parameters[QLatin1String("index")].toInt());
1183 
1184 				if (index >= 0 && index < getHistory().entries.count())
1185 				{
1186 					state.isEnabled = true;
1187 				}
1188 			}
1189 
1190 			break;
1191 		case ActionsManager::StopAction:
1192 			state.isEnabled = (getLoadingState() == OngoingLoadingState);
1193 
1194 			break;
1195 		case ActionsManager::ReloadAction:
1196 			state.isEnabled = (getLoadingState() != OngoingLoadingState);
1197 
1198 			break;
1199 		case ActionsManager::ReloadOrStopAction:
1200 			state = getActionState((getLoadingState() == OngoingLoadingState) ? ActionsManager::StopAction : ActionsManager::ReloadAction);
1201 
1202 			break;
1203 		case ActionsManager::ScheduleReloadAction:
1204 			if (!parameters.contains(QLatin1String("time")) || (parameters.contains(QLatin1String("time")) && parameters[QLatin1String("time")].type() == QVariant::String && parameters[QLatin1String("time")].toString() == QLatin1String("custom")))
1205 			{
1206 				state.isChecked = (m_options.contains(SettingsManager::Content_PageReloadTimeOption) && !QVector<int>({-1, 0, 60, 1800, 3600, 7200}).contains(m_options[SettingsManager::Content_PageReloadTimeOption].toInt()));
1207 			}
1208 			else if (parameters.contains(QLatin1String("time")) && parameters[QLatin1String("time")].type() != QVariant::String)
1209 			{
1210 				const int reloadTime(parameters[QLatin1String("time")].toInt());
1211 
1212 				if (reloadTime < 0)
1213 				{
1214 					state.isChecked = (!m_options.contains(SettingsManager::Content_PageReloadTimeOption) || m_options[SettingsManager::Content_PageReloadTimeOption].toInt() < 0);
1215 					state.text = tr("Page Default");
1216 				}
1217 				else
1218 				{
1219 					state.isChecked = (m_options.contains(SettingsManager::Content_PageReloadTimeOption) && reloadTime == m_options[SettingsManager::Content_PageReloadTimeOption].toInt());
1220 
1221 					if (reloadTime == 0)
1222 					{
1223 						state.text = tr("Never Reload");
1224 					}
1225 					else
1226 					{
1227 						state.text = tr("Reload Every: %n second(s)", "", reloadTime);
1228 					}
1229 				}
1230 			}
1231 
1232 			break;
1233 		case ActionsManager::UndoAction:
1234 			state.isEnabled = canUndo();
1235 
1236 			break;
1237 		case ActionsManager::RedoAction:
1238 			state.isEnabled = canRedo();
1239 
1240 			break;
1241 		case ActionsManager::CutAction:
1242 			state.isEnabled = (this->hasSelection() && !getSelectedText().trimmed().isEmpty() && m_hitResult.flags.testFlag(HitTestResult::IsContentEditableTest));
1243 
1244 			break;
1245 		case ActionsManager::CopyAction:
1246 		case ActionsManager::CopyPlainTextAction:
1247 		case ActionsManager::CopyToNoteAction:
1248 		case ActionsManager::UnselectAction:
1249 			state.isEnabled = (this->hasSelection() && !getSelectedText().trimmed().isEmpty());
1250 
1251 			break;
1252 		case ActionsManager::PasteAction:
1253 			state.isEnabled = (m_hitResult.flags.testFlag(HitTestResult::IsContentEditableTest) && (parameters.contains(QLatin1String("note")) || parameters.contains(QLatin1String("text")) || (QApplication::clipboard()->mimeData() && QApplication::clipboard()->mimeData()->hasText())));
1254 
1255 			break;
1256 		case ActionsManager::PasteAndGoAction:
1257 			state.isEnabled = (QApplication::clipboard()->mimeData() && QApplication::clipboard()->mimeData()->hasText());
1258 
1259 			break;
1260 		case ActionsManager::DeleteAction:
1261 			state.isEnabled = (m_hitResult.flags.testFlag(HitTestResult::IsContentEditableTest) && !m_hitResult.flags.testFlag(HitTestResult::IsEmptyTest) && this->hasSelection() && !getSelectedText().trimmed().isEmpty());
1262 
1263 			break;
1264 		case ActionsManager::SelectAllAction:
1265 			state.isEnabled = !m_hitResult.flags.testFlag(HitTestResult::IsEmptyTest);
1266 
1267 			break;
1268 		case ActionsManager::ClearAllAction:
1269 			state.isEnabled = (m_hitResult.flags.testFlag(HitTestResult::IsContentEditableTest) && !m_hitResult.flags.testFlag(HitTestResult::IsEmptyTest));
1270 
1271 			break;
1272 		case ActionsManager::CheckSpellingAction:
1273 			{
1274 				const QVector<SpellCheckManager::DictionaryInformation> dictionaries(getDictionaries());
1275 
1276 				state.isEnabled = (getOption(SettingsManager::Browser_EnableSpellCheckOption, getUrl()).toBool() && !dictionaries.isEmpty());
1277 
1278 				if (parameters.contains(QLatin1String("dictionary")))
1279 				{
1280 					const QString dictionary(parameters[QLatin1String("dictionary")].toString());
1281 
1282 					state.text = dictionary;
1283 					state.isChecked = (dictionary == (getOption(SettingsManager::Browser_SpellCheckDictionaryOption).isNull() ? SpellCheckManager::getDefaultDictionary() : getOption(SettingsManager::Browser_SpellCheckDictionaryOption).toString()));
1284 
1285 					for (int i = 0; i < dictionaries.count(); ++i)
1286 					{
1287 						if (dictionaries.at(i).name == dictionary)
1288 						{
1289 							state.text = dictionaries.at(i).title;
1290 
1291 							break;
1292 						}
1293 					}
1294 				}
1295 				else
1296 				{
1297 					state.isChecked = (m_hitResult.flags.testFlag(HitTestResult::IsSpellCheckEnabled));
1298 				}
1299 			}
1300 
1301 			break;
1302 		case ActionsManager::SearchAction:
1303 			{
1304 				const SearchEnginesManager::SearchEngineDefinition searchEngine(SearchEnginesManager::getSearchEngine(parameters.contains(QLatin1String("searchEngine")) ? parameters[QLatin1String("searchEngine")].toString() : getOption(SettingsManager::Search_DefaultQuickSearchEngineOption).toString()));
1305 
1306 				state.text = (searchEngine.isValid() ? searchEngine.title : QCoreApplication::translate("actions", "Search"));
1307 				state.icon = (searchEngine.icon.isNull() ? ThemesManager::createIcon(QLatin1String("edit-find")) : searchEngine.icon);
1308 				state.isEnabled = searchEngine.isValid();
1309 			}
1310 
1311 			break;
1312 		case ActionsManager::CreateSearchAction:
1313 			state.isEnabled = m_hitResult.flags.testFlag(HitTestResult::IsFormTest);
1314 
1315 			break;
1316 		case ActionsManager::TakeScreenshotAction:
1317 			state.isEnabled = canTakeScreenshot();
1318 
1319 			break;
1320 		case ActionsManager::BookmarkPageAction:
1321 			state.text = (BookmarksManager::hasBookmark(getUrl()) ? QCoreApplication::translate("actions", "Edit Bookmark…") : QCoreApplication::translate("actions", "Add Bookmark…"));
1322 
1323 			break;
1324 		case ActionsManager::LoadPluginsAction:
1325 			state.isEnabled = (getAmountOfDeferredPlugins() > 0);
1326 
1327 			break;
1328 		case ActionsManager::EnableJavaScriptAction:
1329 			state.isChecked = getOption(SettingsManager::Permissions_EnableJavaScriptOption, getUrl()).toBool();
1330 
1331 			break;
1332 		case ActionsManager::EnableReferrerAction:
1333 			state.isChecked = getOption(SettingsManager::Network_EnableReferrerOption, getUrl()).toBool();
1334 
1335 			break;
1336 		case ActionsManager::ViewSourceAction:
1337 			state.isEnabled = canViewSource();
1338 
1339 			break;
1340 		case ActionsManager::InspectPageAction:
1341 			state.isChecked = isInspecting();
1342 			state.isEnabled = canInspect();
1343 
1344 			break;
1345 		case ActionsManager::InspectElementAction:
1346 			state.isEnabled = canInspect();
1347 
1348 			break;
1349 		case ActionsManager::WebsitePreferencesAction:
1350 			state.isEnabled = (!Utils::isUrlEmpty(getUrl()) && getUrl().scheme() != QLatin1String("about"));
1351 
1352 			break;
1353 		case ActionsManager::ResetQuickPreferencesAction:
1354 			state.isEnabled = !m_options.isEmpty();
1355 
1356 			break;
1357 		default:
1358 			break;
1359 	}
1360 
1361 	return state;
1362 }
1363 
getActiveFrame() const1364 WebWidget::LinkUrl WebWidget::getActiveFrame() const
1365 {
1366 	return {};
1367 }
1368 
getActiveImage() const1369 WebWidget::LinkUrl WebWidget::getActiveImage() const
1370 {
1371 	return {};
1372 }
1373 
getActiveLink() const1374 WebWidget::LinkUrl WebWidget::getActiveLink() const
1375 {
1376 	return {};
1377 }
1378 
getActiveMedia() const1379 WebWidget::LinkUrl WebWidget::getActiveMedia() const
1380 {
1381 	return {};
1382 }
1383 
getSslInformation() const1384 WebWidget::SslInformation WebWidget::getSslInformation() const
1385 {
1386 	return SslInformation();
1387 }
1388 
getStyleSheets() const1389 QStringList WebWidget::getStyleSheets() const
1390 {
1391 	return {};
1392 }
1393 
getDictionaries() const1394 QVector<SpellCheckManager::DictionaryInformation> WebWidget::getDictionaries() const
1395 {
1396 	return SpellCheckManager::getDictionaries();
1397 }
1398 
getFeeds() const1399 QVector<WebWidget::LinkUrl> WebWidget::getFeeds() const
1400 {
1401 	return {};
1402 }
1403 
getLinks() const1404 QVector<WebWidget::LinkUrl> WebWidget::getLinks() const
1405 {
1406 	return {};
1407 }
1408 
getSearchEngines() const1409 QVector<WebWidget::LinkUrl> WebWidget::getSearchEngines() const
1410 {
1411 	return {};
1412 }
1413 
getBlockedRequests() const1414 QVector<NetworkManager::ResourceInformation> WebWidget::getBlockedRequests() const
1415 {
1416 	return {};
1417 }
1418 
getOptions() const1419 QHash<int, QVariant> WebWidget::getOptions() const
1420 {
1421 	return m_options;
1422 }
1423 
getHeaders() const1424 QMap<QByteArray, QByteArray> WebWidget::getHeaders() const
1425 {
1426 	return {};
1427 }
1428 
getMetaData() const1429 QMultiMap<QString, QString> WebWidget::getMetaData() const
1430 {
1431 	return {};
1432 }
1433 
getCurrentHitTestResult() const1434 WebWidget::HitTestResult WebWidget::getCurrentHitTestResult() const
1435 {
1436 	return m_hitResult;
1437 }
1438 
getHitTestResult(const QPoint & position)1439 WebWidget::HitTestResult WebWidget::getHitTestResult(const QPoint &position)
1440 {
1441 	Q_UNUSED(position)
1442 
1443 	return {};
1444 }
1445 
getContentState() const1446 WebWidget::ContentStates WebWidget::getContentState() const
1447 {
1448 	const QUrl url(getUrl());
1449 
1450 	if (url.isEmpty() || url.scheme() == QLatin1String("about"))
1451 	{
1452 		return ApplicationContentState;
1453 	}
1454 
1455 	if (url.scheme() == QLatin1String("file"))
1456 	{
1457 		return LocalContentState;
1458 	}
1459 
1460 	ContentStates state(RemoteContentState);
1461 
1462 	if (getOption(SettingsManager::Security_EnableFraudCheckingOption, url).toBool() && ContentFiltersManager::isFraud(url))
1463 	{
1464 		state |= FraudContentState;
1465 	}
1466 
1467 	return state;
1468 }
1469 
getPermission(FeaturePermission feature,const QUrl & url) const1470 WebWidget::PermissionPolicy WebWidget::getPermission(FeaturePermission feature, const QUrl &url) const
1471 {
1472 	if (!url.isValid())
1473 	{
1474 		return DeniedPermission;
1475 	}
1476 
1477 	QString value;
1478 
1479 	switch (feature)
1480 	{
1481 		case FullScreenFeature:
1482 			value = getOption(SettingsManager::Permissions_EnableFullScreenOption, url).toString();
1483 
1484 			break;
1485 		case GeolocationFeature:
1486 			value = getOption(SettingsManager::Permissions_EnableGeolocationOption, url).toString();
1487 
1488 			break;
1489 		case NotificationsFeature:
1490 			value = getOption(SettingsManager::Permissions_EnableNotificationsOption, url).toString();
1491 
1492 			break;
1493 		case PointerLockFeature:
1494 			value = getOption(SettingsManager::Permissions_EnablePointerLockOption, url).toString();
1495 
1496 			break;
1497 		case CaptureAudioFeature:
1498 			value = getOption(SettingsManager::Permissions_EnableMediaCaptureAudioOption, url).toString();
1499 
1500 			break;
1501 		case CaptureVideoFeature:
1502 			value = getOption(SettingsManager::Permissions_EnableMediaCaptureVideoOption, url).toString();
1503 
1504 			break;
1505 		case CaptureAudioVideoFeature:
1506 			{
1507 				const QString captureAudioValue(getOption(SettingsManager::Permissions_EnableMediaCaptureAudioOption, url).toString());
1508 				const QString captureVideoValue(getOption(SettingsManager::Permissions_EnableMediaCaptureVideoOption, url).toString());
1509 
1510 				if (captureAudioValue == QLatin1String("allow") && captureVideoValue == QLatin1String("allow"))
1511 				{
1512 					value = QLatin1String("allow");
1513 				}
1514 				else if (captureAudioValue == QLatin1String("disallow") || captureVideoValue == QLatin1String("disallow"))
1515 				{
1516 					value = QLatin1String("disallow");
1517 				}
1518 				else
1519 				{
1520 					value = QLatin1String("ask");
1521 				}
1522 			}
1523 
1524 			break;
1525 		case PlaybackAudioFeature:
1526 			value = getOption(SettingsManager::Permissions_EnableMediaPlaybackAudioOption, url).toString();
1527 
1528 			break;
1529 		default:
1530 			return DeniedPermission;
1531 	}
1532 
1533 	if (value == QLatin1String("allow"))
1534 	{
1535 		return GrantedPermission;
1536 	}
1537 
1538 	if (value == QLatin1String("disallow"))
1539 	{
1540 		return DeniedPermission;
1541 	}
1542 
1543 	return KeepAskingPermission;
1544 }
1545 
getWindowIdentifier() const1546 quint64 WebWidget::getWindowIdentifier() const
1547 {
1548 	return m_windowIdentifier;
1549 }
1550 
getAmountOfDeferredPlugins() const1551 int WebWidget::getAmountOfDeferredPlugins() const
1552 {
1553 	return 0;
1554 }
1555 
canGoBack() const1556 bool WebWidget::canGoBack() const
1557 {
1558 	return false;
1559 }
1560 
canGoForward() const1561 bool WebWidget::canGoForward() const
1562 {
1563 	return false;
1564 }
1565 
canFastForward() const1566 bool WebWidget::canFastForward() const
1567 {
1568 	return false;
1569 }
1570 
canInspect() const1571 bool WebWidget::canInspect() const
1572 {
1573 	return false;
1574 }
1575 
canTakeScreenshot() const1576 bool WebWidget::canTakeScreenshot() const
1577 {
1578 	return false;
1579 }
1580 
canRedo() const1581 bool WebWidget::canRedo() const
1582 {
1583 	return false;
1584 }
1585 
canUndo() const1586 bool WebWidget::canUndo() const
1587 {
1588 	return false;
1589 }
1590 
canShowContextMenu(const QPoint & position) const1591 bool WebWidget::canShowContextMenu(const QPoint &position) const
1592 {
1593 	Q_UNUSED(position)
1594 
1595 	return true;
1596 }
1597 
canViewSource() const1598 bool WebWidget::canViewSource() const
1599 {
1600 	return true;
1601 }
1602 
isInspecting() const1603 bool WebWidget::isInspecting() const
1604 {
1605 	return false;
1606 }
1607 
isPopup() const1608 bool WebWidget::isPopup() const
1609 {
1610 	return false;
1611 }
1612 
isScrollBar(const QPoint & position) const1613 bool WebWidget::isScrollBar(const QPoint &position) const
1614 {
1615 	Q_UNUSED(position)
1616 
1617 	return false;
1618 }
1619 
hasOption(int identifier) const1620 bool WebWidget::hasOption(int identifier) const
1621 {
1622 	return m_options.contains(identifier);
1623 }
1624 
hasSelection() const1625 bool WebWidget::hasSelection() const
1626 {
1627 	return false;
1628 }
1629 
hasWatchedChanges(ChangeWatcher watcher) const1630 bool WebWidget::hasWatchedChanges(ChangeWatcher watcher) const
1631 {
1632 	Q_UNUSED(watcher)
1633 
1634 	return false;
1635 }
1636 
isAudible() const1637 bool WebWidget::isAudible() const
1638 {
1639 	return false;
1640 }
1641 
isAudioMuted() const1642 bool WebWidget::isAudioMuted() const
1643 {
1644 	return false;
1645 }
1646 
isFullScreen() const1647 bool WebWidget::isFullScreen() const
1648 {
1649 	return false;
1650 }
1651 
isWatchingChanges(ChangeWatcher watcher) const1652 bool WebWidget::isWatchingChanges(ChangeWatcher watcher) const
1653 {
1654 	return m_changeWatchers.contains(watcher);
1655 }
1656 
1657 }
1658