1
mirror of https://invent.kde.org/network/falkon.git synced 2024-11-14 11:02:19 +01:00
falkonOfficial/src/lib/webengine/webview.cpp
2016-04-06 17:24:15 +02:00

1201 lines
35 KiB
C++

/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2010-2016 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "webview.h"
#include "webpage.h"
#include "mainapplication.h"
#include "qztools.h"
#include "iconprovider.h"
#include "history.h"
#include "pluginproxy.h"
#include "downloadmanager.h"
#include "siteinfo.h"
#include "searchenginesmanager.h"
#include "browsinglibrary.h"
#include "bookmarkstools.h"
#include "settings.h"
#include "qzsettings.h"
#include "enhancedmenu.h"
#include "locationbar.h"
#include "webinspector.h"
#include "scripts.h"
#include "webhittestresult.h"
#include "iconloader.h"
#include <iostream>
#include <QDir>
#include <QTimer>
#include <QDesktopServices>
#include <QWebEngineHistory>
#include <QClipboard>
#include <QHostInfo>
#include <QMimeData>
bool WebView::s_forceContextMenuOnMouseRelease = false;
WebView::WebView(QWidget* parent)
: QWebEngineView(parent)
, m_siteIconLoader(0)
, m_progress(100)
, m_page(0)
, m_firstLoad(false)
, m_rwhvqt(0)
{
connect(this, &QWebEngineView::loadStarted, this, &WebView::slotLoadStarted);
connect(this, &QWebEngineView::loadProgress, this, &WebView::slotLoadProgress);
connect(this, &QWebEngineView::loadFinished, this, &WebView::slotLoadFinished);
connect(this, &QWebEngineView::urlChanged, this, &WebView::slotUrlChanged);
connect(this, &QWebEngineView::iconUrlChanged, this, &WebView::slotIconUrlChanged);
m_currentZoomLevel = zoomLevels().indexOf(100);
setAcceptDrops(true);
installEventFilter(this);
WebInspector::registerView(this);
}
WebView::~WebView()
{
WebInspector::unregisterView(this);
}
QIcon WebView::icon() const
{
if (!m_siteIcon.isNull()) {
return m_siteIcon;
}
if (url().scheme() == QLatin1String("ftp")) {
return IconProvider::standardIcon(QStyle::SP_ComputerIcon);
}
if (url().scheme() == QLatin1String("file")) {
return IconProvider::standardIcon(QStyle::SP_DriveHDIcon);
}
return IconProvider::iconForUrl(url());
}
QString WebView::title() const
{
QString title = QWebEngineView::title();
if (title.isEmpty()) {
title = url().toString(QUrl::RemoveFragment);
}
if (title.isEmpty() || title == QLatin1String("about:blank")) {
return tr("Empty Page");
}
return title;
}
bool WebView::isTitleEmpty() const
{
return QWebEngineView::title().isEmpty();
}
WebPage* WebView::page() const
{
return m_page;
}
void WebView::setPage(WebPage *page)
{
if (m_page == page) {
return;
}
m_page = page;
QWebEngineView::setPage(m_page);
connect(m_page, SIGNAL(privacyChanged(bool)), this, SIGNAL(privacyChanged(bool)));
// Set default zoom level
zoomReset();
// Actions needs to be initialized for every QWebPage change
initializeActions();
mApp->plugins()->emitWebPageCreated(m_page);
}
void WebView::load(const QUrl &url)
{
QWebEngineView::load(url);
if (!m_firstLoad) {
m_firstLoad = true;
WebInspector::pushView(this);
}
}
void WebView::load(const LoadRequest &request)
{
const QUrl reqUrl = request.url();
if (reqUrl.isEmpty())
return;
if (reqUrl.scheme() == QL1S("javascript")) {
const QString scriptSource = reqUrl.toString().mid(11);
// Is the javascript source percent encoded or not?
// Looking for % character in source should work in most cases
if (scriptSource.contains(QL1C('%')))
page()->runJavaScript(QUrl::fromPercentEncoding(scriptSource.toUtf8()));
else
page()->runJavaScript(scriptSource);
return;
}
if (isUrlValid(reqUrl)) {
loadRequest(request);
return;
}
// Make sure to correctly load hosts like localhost (eg. without the dot)
if (!reqUrl.isEmpty() &&
reqUrl.scheme().isEmpty() &&
!QzTools::containsSpace(reqUrl.path()) && // See #1622
!reqUrl.path().contains(QL1C('.'))
) {
QUrl u(QSL("http://") + reqUrl.path());
if (u.isValid()) {
// This is blocking...
QHostInfo info = QHostInfo::fromName(u.path());
if (info.error() == QHostInfo::NoError) {
LoadRequest req = request;
req.setUrl(u);
loadRequest(req);
return;
}
}
}
if (qzSettings->searchFromAddressBar) {
const LoadRequest searchRequest = mApp->searchEnginesManager()->searchResult(request.urlString());
loadRequest(searchRequest);
}
}
bool WebView::isLoading() const
{
return m_progress < 100;
}
int WebView::loadingProgress() const
{
return m_progress;
}
void WebView::fakeLoadingProgress(int progress)
{
emit loadStarted();
emit loadProgress(progress);
}
int WebView::zoomLevel() const
{
return m_currentZoomLevel;
}
void WebView::setZoomLevel(int level)
{
m_currentZoomLevel = level;
applyZoom();
}
QPoint WebView::mapToViewport(const QPoint &pos) const
{
return page()->mapToViewport(pos);
}
void WebView::restoreHistory(const QByteArray &data)
{
QDataStream stream(data);
stream >> *history();
// Workaround clearing QWebChannel after restoring history
page()->setupWebChannel();
}
QWidget *WebView::inputWidget() const
{
return m_rwhvqt;
}
// static
bool WebView::isUrlValid(const QUrl &url)
{
// Valid url must have scheme and actually contains something (therefore scheme:// is invalid)
return url.isValid() && !url.scheme().isEmpty() && (!url.host().isEmpty() || !url.path().isEmpty() || url.hasQuery());
}
// static
QList<int> WebView::zoomLevels()
{
return QList<int>() << 30 << 40 << 50 << 67 << 80 << 90 << 100
<< 110 << 120 << 133 << 150 << 170 << 200
<< 220 << 233 << 250 << 270 << 285 << 300;
}
// static
bool WebView::forceContextMenuOnMouseRelease()
{
return s_forceContextMenuOnMouseRelease;
}
// static
void WebView::setForceContextMenuOnMouseRelease(bool force)
{
s_forceContextMenuOnMouseRelease = force;
}
void WebView::addNotification(QWidget* notif)
{
emit showNotification(notif);
}
void WebView::applyZoom()
{
setZoomFactor(qreal(zoomLevels().at(m_currentZoomLevel)) / 100.0);
emit zoomLevelChanged(m_currentZoomLevel);
}
void WebView::zoomIn()
{
if (m_currentZoomLevel < zoomLevels().count() - 1) {
m_currentZoomLevel++;
applyZoom();
}
}
void WebView::zoomOut()
{
if (m_currentZoomLevel > 0) {
m_currentZoomLevel--;
applyZoom();
}
}
void WebView::zoomReset()
{
if (m_currentZoomLevel != qzSettings->defaultZoomLevel) {
m_currentZoomLevel = qzSettings->defaultZoomLevel;
applyZoom();
}
}
void WebView::editUndo()
{
triggerPageAction(QWebEnginePage::Undo);
}
void WebView::editRedo()
{
triggerPageAction(QWebEnginePage::Redo);
}
void WebView::editCut()
{
triggerPageAction(QWebEnginePage::Cut);
}
void WebView::editCopy()
{
triggerPageAction(QWebEnginePage::Copy);
}
void WebView::editPaste()
{
triggerPageAction(QWebEnginePage::Paste);
}
void WebView::editSelectAll()
{
triggerPageAction(QWebEnginePage::SelectAll);
}
void WebView::editDelete()
{
QKeyEvent ev(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier);
QApplication::sendEvent(this, &ev);
}
void WebView::reloadBypassCache()
{
triggerPageAction(QWebEnginePage::ReloadAndBypassCache);
}
void WebView::back()
{
QWebEngineHistory* history = page()->history();
if (history->canGoBack()) {
history->back();
emit urlChanged(url());
}
}
void WebView::forward()
{
QWebEngineHistory* history = page()->history();
if (history->canGoForward()) {
history->forward();
emit urlChanged(url());
}
}
void WebView::slotLoadStarted()
{
m_progress = 0;
}
void WebView::slotLoadProgress(int progress)
{
m_progress = progress;
}
void WebView::slotLoadFinished(bool ok)
{
m_progress = 100;
if (ok)
mApp->history()->addHistoryEntry(this);
}
void WebView::slotUrlChanged(const QUrl &url)
{
Q_UNUSED(url)
// Don't save blank page / speed dial in tab history
if (!history()->canGoForward() && history()->backItems(1).size() == 1) {
const QString s = LocationBar::convertUrlToText(history()->backItem().url());
if (s.isEmpty())
history()->clear();
}
}
void WebView::slotIconUrlChanged(const QUrl &url)
{
m_siteIcon = QIcon();
delete m_siteIconLoader;
m_siteIconLoader = new IconLoader(url, this);
connect(m_siteIconLoader, &IconLoader::iconLoaded, [this, url](const QIcon &icon) {
m_siteIcon = icon;
emit iconChanged();
IconProvider::instance()->saveIcon(this);
});
}
void WebView::openUrlInNewWindow()
{
if (QAction* action = qobject_cast<QAction*>(sender())) {
mApp->createWindow(Qz::BW_NewWindow, action->data().toUrl());
}
}
void WebView::sendLinkByMail()
{
if (QAction* action = qobject_cast<QAction*>(sender())) {
const QUrl mailUrl = QUrl::fromEncoded("mailto:%20?body=" + QUrl::toPercentEncoding(action->data().toUrl().toEncoded()));
QDesktopServices::openUrl(mailUrl);
}
}
void WebView::sendPageByMail()
{
const QUrl mailUrl = QUrl::fromEncoded("mailto:%20?body=" + QUrl::toPercentEncoding(url().toEncoded()) + "&subject=" + QUrl::toPercentEncoding(title()));
QDesktopServices::openUrl(mailUrl);
}
void WebView::copyLinkToClipboard()
{
if (QAction* action = qobject_cast<QAction*>(sender())) {
QApplication::clipboard()->setText(action->data().toUrl().toEncoded());
}
}
void WebView::downloadLinkToDisk()
{
triggerPageAction(QWebEnginePage::DownloadLinkToDisk);
}
void WebView::downloadImageToDisk()
{
triggerPageAction(QWebEnginePage::DownloadImageToDisk);
}
void WebView::downloadMediaToDisk()
{
triggerPageAction(QWebEnginePage::DownloadMediaToDisk);
}
void WebView::openUrlInNewTab(const QUrl &url, Qz::NewTabPositionFlags position)
{
loadInNewTab(url, position);
}
void WebView::openActionUrl()
{
if (QAction* action = qobject_cast<QAction*>(sender())) {
load(action->data().toUrl());
}
}
void WebView::showSource()
{
// view-source: doesn't work on itself and custom schemes
if (url().scheme() == QL1S("view-source") || url().scheme() == QL1S("qupzilla") || url().scheme() == QL1S("qrc")) {
page()->toHtml([](const QString &html) {
std::cout << html.toLocal8Bit().constData() << std::endl;
});
return;
}
QUrl u;
u.setScheme(QSL("view-source"));
u.setPath(url().toString());
openUrlInNewTab(u, Qz::NT_SelectedTab);
}
void WebView::showSiteInfo()
{
SiteInfo* s = new SiteInfo(this);
s->show();
}
void WebView::searchSelectedText()
{
SearchEngine engine = mApp->searchEnginesManager()->activeEngine();
if (QAction* act = qobject_cast<QAction*>(sender())) {
if (act->data().isValid()) {
engine = act->data().value<SearchEngine>();
}
}
const LoadRequest req = mApp->searchEnginesManager()->searchResult(engine, selectedText());
loadInNewTab(req, Qz::NT_SelectedTab);
}
void WebView::searchSelectedTextInBackgroundTab()
{
SearchEngine engine = mApp->searchEnginesManager()->activeEngine();
if (QAction* act = qobject_cast<QAction*>(sender())) {
if (act->data().isValid()) {
engine = act->data().value<SearchEngine>();
}
}
const LoadRequest req = mApp->searchEnginesManager()->searchResult(engine, selectedText());
loadInNewTab(req, Qz::NT_NotSelectedTab);
}
void WebView::bookmarkLink()
{
if (QAction* action = qobject_cast<QAction*>(sender())) {
if (action->data().isNull()) {
BookmarksTools::addBookmarkDialog(this, url(), title());
}
else {
const QVariantList bData = action->data().value<QVariantList>();
const QString bookmarkTitle = bData.at(1).toString().isEmpty() ? title() : bData.at(1).toString();
BookmarksTools::addBookmarkDialog(this, bData.at(0).toUrl(), bookmarkTitle);
}
}
}
void WebView::openUrlInSelectedTab()
{
if (QAction* action = qobject_cast<QAction*>(sender())) {
openUrlInNewTab(action->data().toUrl(), Qz::NT_CleanSelectedTab);
}
}
void WebView::openUrlInBackgroundTab()
{
if (QAction* action = qobject_cast<QAction*>(sender())) {
openUrlInNewTab(action->data().toUrl(), Qz::NT_CleanNotSelectedTab);
}
}
void WebView::userDefinedOpenUrlInNewTab(const QUrl &url, bool invert)
{
Qz::NewTabPositionFlags position = qzSettings->newTabPosition;
if (invert) {
if (position & Qz::NT_SelectedTab) {
position &= ~Qz::NT_SelectedTab;
position |= Qz::NT_NotSelectedTab;
}
else {
position &= ~Qz::NT_NotSelectedTab;
position |= Qz::NT_SelectedTab;
}
}
QUrl actionUrl;
if (!url.isEmpty()) {
actionUrl = url;
}
else if (QAction* action = qobject_cast<QAction*>(sender())) {
actionUrl = action->data().toUrl();
}
openUrlInNewTab(actionUrl, position);
}
void WebView::userDefinedOpenUrlInBgTab(const QUrl &url)
{
QUrl actionUrl;
if (!url.isEmpty()) {
actionUrl = url;
}
else if (QAction* action = qobject_cast<QAction*>(sender())) {
actionUrl = action->data().toUrl();
}
userDefinedOpenUrlInNewTab(actionUrl, true);
}
void WebView::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasUrls()) {
event->accept();
return;
}
QWebEngineView::dragEnterEvent(event);
}
void WebView::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasUrls()) {
const QList<QUrl> &urls = event->mimeData()->urls();
load(urls.at(0));
for (int i = 1; i < urls.size(); ++i) {
openUrlInNewTab(urls.at(i), Qz::NT_CleanSelectedTab);
}
return;
}
QWebEngineView::dropEvent(event);
}
void WebView::createContextMenu(QMenu *menu, const WebHitTestResult &hitTest)
{
// cppcheck-suppress variableScope
int spellCheckActionCount = 0;
if (!hitTest.linkUrl().isEmpty() && hitTest.linkUrl().scheme() != QL1S("javascript")) {
createLinkContextMenu(menu, hitTest);
}
if (!hitTest.imageUrl().isEmpty()) {
createImageContextMenu(menu, hitTest);
}
if (!hitTest.mediaUrl().isEmpty()) {
createMediaContextMenu(menu, hitTest);
}
if (hitTest.isContentEditable()) {
// This only checks if the menu is empty (only spellchecker actions added)
if (menu->actions().count() == spellCheckActionCount) {
menu->addAction(pageAction(QWebEnginePage::Undo));
menu->addAction(pageAction(QWebEnginePage::Redo));
menu->addSeparator();
menu->addAction(pageAction(QWebEnginePage::Cut));
menu->addAction(pageAction(QWebEnginePage::Copy));
menu->addAction(pageAction(QWebEnginePage::Paste));
}
if (hitTest.tagName() == QL1S("input")) {
QAction *act = menu->addAction(QString());
act->setVisible(false);
checkForForm(act, hitTest.pos());
}
}
if (!selectedText().isEmpty()) {
createSelectedTextContextMenu(menu, hitTest);
}
if (menu->isEmpty()) {
createPageContextMenu(menu);
}
menu->addSeparator();
mApp->plugins()->populateWebViewMenu(menu, this, hitTest);
}
void WebView::createPageContextMenu(QMenu* menu)
{
QAction* action = menu->addAction(tr("&Back"), this, SLOT(back()));
action->setIcon(IconProvider::standardIcon(QStyle::SP_ArrowBack));
action->setEnabled(history()->canGoBack());
action = menu->addAction(tr("&Forward"), this, SLOT(forward()));
action->setIcon(IconProvider::standardIcon(QStyle::SP_ArrowForward));
action->setEnabled(history()->canGoForward());
// Special menu for Speed Dial page
if (url().toString() == QL1S("qupzilla:speeddial")) {
menu->addSeparator();
menu->addAction(QIcon::fromTheme("list-add"), tr("&Add New Page"), this, SLOT(addSpeedDial()));
menu->addAction(IconProvider::settingsIcon(), tr("&Configure Speed Dial"), this, SLOT(configureSpeedDial()));
menu->addSeparator();
menu->addAction(QIcon::fromTheme(QSL("view-refresh")), tr("Reload All Dials"), this, SLOT(reloadAllSpeedDials()));
return;
}
menu->addAction(pageAction(QWebEnginePage::Reload));
menu->addAction(pageAction(QWebEnginePage::Stop));
menu->addSeparator();
menu->addAction(QIcon::fromTheme("bookmark-new"), tr("Book&mark page"), this, SLOT(bookmarkLink()));
menu->addAction(QIcon::fromTheme("edit-copy"), tr("&Copy page link"), this, SLOT(copyLinkToClipboard()))->setData(url());
menu->addAction(QIcon::fromTheme("mail-message-new"), tr("Send page link..."), this, SLOT(sendPageByMail()));
menu->addSeparator();
menu->addAction(QIcon::fromTheme("edit-select-all"), tr("Select &all"), this, SLOT(editSelectAll()));
menu->addSeparator();
if (url().scheme() == QLatin1String("http") || url().scheme() == QLatin1String("https")) {
const QUrl w3url = QUrl::fromEncoded("http://validator.w3.org/check?uri=" + QUrl::toPercentEncoding(url().toEncoded()));
menu->addAction(QIcon(":icons/sites/w3.png"), tr("Validate page"), this, SLOT(openUrlInSelectedTab()))->setData(w3url);
QByteArray langCode = mApp->currentLanguage().left(2).toUtf8();
const QUrl gturl = QUrl::fromEncoded("http://translate.google.com/translate?sl=auto&tl=" + langCode + "&u=" + QUrl::toPercentEncoding(url().toEncoded()));
menu->addAction(QIcon(":icons/sites/translate.png"), tr("Translate page"), this, SLOT(openUrlInSelectedTab()))->setData(gturl);
}
menu->addSeparator();
menu->addAction(QIcon::fromTheme("text-html"), tr("Show so&urce code"), this, SLOT(showSource()));
if (SiteInfo::canShowSiteInfo(url()))
menu->addAction(QIcon::fromTheme("dialog-information"), tr("Show info ab&out site"), this, SLOT(showSiteInfo()));
}
void WebView::createLinkContextMenu(QMenu* menu, const WebHitTestResult &hitTest)
{
menu->addSeparator();
Action* act = new Action(IconProvider::newTabIcon(), tr("Open link in new &tab"));
act->setData(hitTest.linkUrl());
connect(act, SIGNAL(triggered()), this, SLOT(userDefinedOpenUrlInNewTab()));
connect(act, SIGNAL(ctrlTriggered()), this, SLOT(userDefinedOpenUrlInBgTab()));
menu->addAction(act);
menu->addAction(IconProvider::newWindowIcon(), tr("Open link in new &window"), this, SLOT(openUrlInNewWindow()))->setData(hitTest.linkUrl());
menu->addAction(IconProvider::privateBrowsingIcon(), tr("Open link in &private window"), mApp, SLOT(startPrivateBrowsing()))->setData(hitTest.linkUrl());
menu->addSeparator();
QVariantList bData;
bData << hitTest.linkUrl() << hitTest.linkTitle();
menu->addAction(QIcon::fromTheme("bookmark-new"), tr("B&ookmark link"), this, SLOT(bookmarkLink()))->setData(bData);
menu->addAction(QIcon::fromTheme("document-save"), tr("&Save link as..."), this, SLOT(downloadLinkToDisk()));
menu->addAction(QIcon::fromTheme("mail-message-new"), tr("Send link..."), this, SLOT(sendLinkByMail()))->setData(hitTest.linkUrl());
menu->addAction(QIcon::fromTheme("edit-copy"), tr("&Copy link address"), this, SLOT(copyLinkToClipboard()))->setData(hitTest.linkUrl());
menu->addSeparator();
if (!selectedText().isEmpty()) {
pageAction(QWebEnginePage::Copy)->setIcon(QIcon::fromTheme("edit-copy"));
menu->addAction(pageAction(QWebEnginePage::Copy));
}
}
void WebView::createImageContextMenu(QMenu* menu, const WebHitTestResult &hitTest)
{
menu->addSeparator();
Action* act = new Action(tr("Show i&mage"));
act->setData(hitTest.imageUrl());
connect(act, SIGNAL(triggered()), this, SLOT(openActionUrl()));
connect(act, SIGNAL(ctrlTriggered()), this, SLOT(userDefinedOpenUrlInNewTab()));
menu->addAction(act);
menu->addAction(QIcon::fromTheme("edit-copy"), tr("Copy image ad&dress"), this, SLOT(copyLinkToClipboard()))->setData(hitTest.imageUrl());
menu->addSeparator();
menu->addAction(QIcon::fromTheme("document-save"), tr("&Save image as..."), this, SLOT(downloadImageToDisk()));
menu->addAction(QIcon::fromTheme("mail-message-new"), tr("Send image..."), this, SLOT(sendLinkByMail()))->setData(hitTest.imageUrl());
menu->addSeparator();
if (!selectedText().isEmpty()) {
pageAction(QWebEnginePage::Copy)->setIcon(QIcon::fromTheme("edit-copy"));
menu->addAction(pageAction(QWebEnginePage::Copy));
}
}
void WebView::createSelectedTextContextMenu(QMenu* menu, const WebHitTestResult &hitTest)
{
Q_UNUSED(hitTest)
QString selectedText = page()->selectedText();
menu->addSeparator();
if (!menu->actions().contains(pageAction(QWebEnginePage::Copy))) {
menu->addAction(pageAction(QWebEnginePage::Copy));
}
menu->addAction(QIcon::fromTheme("mail-message-new"), tr("Send text..."), this, SLOT(sendLinkByMail()))->setData(selectedText);
menu->addSeparator();
QString langCode = mApp->currentLanguage().left(2).toUtf8();
QUrl googleTranslateUrl = QUrl(QString("https://translate.google.com/#auto/%1/%2").arg(langCode, selectedText));
Action* gtwact = new Action(QIcon(":icons/sites/translate.png"), tr("Google Translate"));
gtwact->setData(googleTranslateUrl);
connect(gtwact, SIGNAL(triggered()), this, SLOT(openUrlInSelectedTab()));
connect(gtwact, SIGNAL(ctrlTriggered()), this, SLOT(openUrlInBackgroundTab()));
menu->addAction(gtwact);
Action* dictact = new Action(QIcon::fromTheme("accessories-dictionary"), tr("Dictionary"));
dictact->setData(QUrl("http://" + (!langCode.isEmpty() ? langCode + "." : langCode) + "wiktionary.org/wiki/Special:Search?search=" + selectedText));
connect(dictact, SIGNAL(triggered()), this, SLOT(openUrlInSelectedTab()));
connect(dictact, SIGNAL(ctrlTriggered()), this, SLOT(openUrlInBackgroundTab()));
menu->addAction(dictact);
// #379: Remove newlines
QString selectedString = selectedText.trimmed().remove(QLatin1Char('\n'));
if (!selectedString.contains(QLatin1Char('.'))) {
// Try to add .com
selectedString.append(QLatin1String(".com"));
}
QUrl guessedUrl = QUrl::fromUserInput(selectedString);
if (isUrlValid(guessedUrl)) {
Action* act = new Action(QIcon::fromTheme("document-open-remote"), tr("Go to &web address"));
act->setData(guessedUrl);
connect(act, SIGNAL(triggered()), this, SLOT(openActionUrl()));
connect(act, SIGNAL(ctrlTriggered()), this, SLOT(userDefinedOpenUrlInNewTab()));
menu->addAction(act);
}
menu->addSeparator();
selectedText.truncate(20);
// KDE is displaying newlines in menu actions ... weird -,-
selectedText.replace(QLatin1Char('\n'), QLatin1Char(' ')).replace(QLatin1Char('\t'), QLatin1Char(' '));
SearchEngine engine = mApp->searchEnginesManager()->activeEngine();
Action* act = new Action(engine.icon, tr("Search \"%1 ..\" with %2").arg(selectedText, engine.name));
connect(act, SIGNAL(triggered()), this, SLOT(searchSelectedText()));
connect(act, SIGNAL(ctrlTriggered()), this, SLOT(searchSelectedTextInBackgroundTab()));
menu->addAction(act);
// Search with ...
Menu* swMenu = new Menu(tr("Search with..."), menu);
swMenu->setCloseOnMiddleClick(true);
SearchEnginesManager* searchManager = mApp->searchEnginesManager();
foreach (const SearchEngine &en, searchManager->allEngines()) {
Action* act = new Action(en.icon, en.name);
act->setData(QVariant::fromValue(en));
connect(act, SIGNAL(triggered()), this, SLOT(searchSelectedText()));
connect(act, SIGNAL(ctrlTriggered()), this, SLOT(searchSelectedTextInBackgroundTab()));
swMenu->addAction(act);
}
menu->addMenu(swMenu);
}
void WebView::createMediaContextMenu(QMenu *menu, const WebHitTestResult &hitTest)
{
bool paused = hitTest.mediaPaused();
bool muted = hitTest.mediaMuted();
menu->addSeparator();
menu->addAction(paused ? tr("&Play") : tr("&Pause"), this, SLOT(toggleMediaPause()))->setIcon(QIcon::fromTheme(paused ? "media-playback-start" : "media-playback-pause"));
menu->addAction(muted ? tr("Un&mute") : tr("&Mute"), this, SLOT(toggleMediaMute()))->setIcon(QIcon::fromTheme(muted ? "audio-volume-muted" : "audio-volume-high"));
menu->addSeparator();
menu->addAction(QIcon::fromTheme("edit-copy"), tr("&Copy Media Address"), this, SLOT(copyLinkToClipboard()))->setData(hitTest.mediaUrl());
menu->addAction(QIcon::fromTheme("mail-message-new"), tr("&Send Media Address"), this, SLOT(sendLinkByMail()))->setData(hitTest.mediaUrl());
menu->addAction(QIcon::fromTheme("document-save"), tr("Save Media To &Disk"), this, SLOT(downloadMediaToDisk()));
}
void WebView::checkForForm(QAction *action, const QPoint &pos)
{
m_clickedPos = mapToViewport(pos);
QPointer<QAction> act = action;
page()->runJavaScript(Scripts::getFormData(m_clickedPos), [this, act](const QVariant &res) {
const QVariantMap &map = res.toMap();
if (!act || map.isEmpty())
return;
const QUrl url = map.value(QSL("action")).toUrl();
const QString method = map.value(QSL("method")).toString();
if (!url.isEmpty() && (method == QL1S("get") || method == QL1S("post"))) {
act->setVisible(true);
act->setIcon(QIcon(QSL(":icons/menu/search-icon.png")));
act->setText(tr("Create Search Engine"));
connect(act.data(), &QAction::triggered, this, &WebView::createSearchEngine);
}
});
}
void WebView::createSearchEngine()
{
page()->runJavaScript(Scripts::getFormData(m_clickedPos), [this](const QVariant &res) {
mApp->searchEnginesManager()->addEngineFromForm(res.toMap(), this);
});
}
void WebView::addSpeedDial()
{
page()->runJavaScript("addSpeedDial()");
}
void WebView::configureSpeedDial()
{
page()->runJavaScript("configureSpeedDial()");
}
void WebView::reloadAllSpeedDials()
{
page()->runJavaScript("reloadAll()");
}
void WebView::toggleMediaPause()
{
triggerPageAction(QWebEnginePage::ToggleMediaPlayPause);
}
void WebView::toggleMediaMute()
{
triggerPageAction(QWebEnginePage::ToggleMediaMute);
}
void WebView::initializeActions()
{
QAction* undoAction = pageAction(QWebEnginePage::Undo);
undoAction->setText(tr("&Undo"));
undoAction->setShortcut(QKeySequence("Ctrl+Z"));
undoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
undoAction->setIcon(QIcon::fromTheme(QSL("edit-undo")));
QAction* redoAction = pageAction(QWebEnginePage::Redo);
redoAction->setText(tr("&Redo"));
redoAction->setShortcut(QKeySequence("Ctrl+Shift+Z"));
redoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
redoAction->setIcon(QIcon::fromTheme(QSL("edit-redo")));
QAction* cutAction = pageAction(QWebEnginePage::Cut);
cutAction->setText(tr("&Cut"));
cutAction->setShortcut(QKeySequence("Ctrl+X"));
cutAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
cutAction->setIcon(QIcon::fromTheme(QSL("edit-cut")));
QAction* copyAction = pageAction(QWebEnginePage::Copy);
copyAction->setText(tr("&Copy"));
copyAction->setShortcut(QKeySequence("Ctrl+C"));
copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
copyAction->setIcon(QIcon::fromTheme(QSL("edit-copy")));
QAction* pasteAction = pageAction(QWebEnginePage::Paste);
pasteAction->setText(tr("&Paste"));
pasteAction->setShortcut(QKeySequence("Ctrl+V"));
pasteAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
pasteAction->setIcon(QIcon::fromTheme(QSL("edit-paste")));
QAction* selectAllAction = pageAction(QWebEnginePage::SelectAll);
selectAllAction->setText(tr("Select All"));
selectAllAction->setShortcut(QKeySequence("Ctrl+A"));
selectAllAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
selectAllAction->setIcon(QIcon::fromTheme(QSL("edit-select-all")));
QAction* reloadAction = pageAction(QWebEnginePage::Reload);
reloadAction->setText(tr("&Reload"));
reloadAction->setIcon(QIcon::fromTheme(QSL("view-refresh")));
QAction* stopAction = pageAction(QWebEnginePage::Stop);
stopAction->setText(tr("S&top"));
stopAction->setIcon(QIcon::fromTheme(QSL("process-stop")));
// Make action shortcuts available for webview
addAction(undoAction);
addAction(redoAction);
addAction(cutAction);
addAction(copyAction);
addAction(pasteAction);
addAction(selectAllAction);
}
void WebView::_wheelEvent(QWheelEvent *event)
{
if (mApp->plugins()->processWheelEvent(Qz::ON_WebView, this, event)) {
event->accept();
return;
}
if (event->modifiers() & Qt::ControlModifier) {
event->delta() > 0 ? zoomIn() : zoomOut();
event->accept();
}
}
void WebView::_mousePressEvent(QMouseEvent *event)
{
m_clickedUrl = QUrl();
m_clickedPos = QPoint();
if (mApp->plugins()->processMousePress(Qz::ON_WebView, this, event)) {
event->accept();
return;
}
switch (event->button()) {
case Qt::XButton1:
back();
event->accept();
break;
case Qt::XButton2:
forward();
event->accept();
break;
case Qt::MiddleButton:
m_clickedUrl = page()->hitTestContent(event->pos()).linkUrl();
if (!m_clickedUrl.isEmpty())
event->accept();
break;
case Qt::LeftButton:
m_clickedUrl = page()->hitTestContent(event->pos()).linkUrl();
break;
default:
break;
}
}
void WebView::_mouseReleaseEvent(QMouseEvent *event)
{
if (mApp->plugins()->processMouseRelease(Qz::ON_WebView, this, event)) {
event->accept();
return;
}
switch (event->button()) {
case Qt::MiddleButton:
if (!m_clickedUrl.isEmpty()) {
const QUrl link = page()->hitTestContent(event->pos()).linkUrl();
if (m_clickedUrl == link && isUrlValid(link)) {
userDefinedOpenUrlInNewTab(link, event->modifiers() & Qt::ShiftModifier);
event->accept();
}
}
break;
case Qt::LeftButton:
if (!m_clickedUrl.isEmpty()) {
const QUrl link = page()->hitTestContent(event->pos()).linkUrl();
if (m_clickedUrl == link && isUrlValid(link)) {
if (event->modifiers() & Qt::ControlModifier) {
userDefinedOpenUrlInNewTab(link, event->modifiers() & Qt::ShiftModifier);
event->accept();
}
}
}
break;
case Qt::RightButton:
if (s_forceContextMenuOnMouseRelease) {
QContextMenuEvent ev(QContextMenuEvent::Mouse, event->pos(), event->globalPos(), event->modifiers());
_contextMenuEvent(&ev);
event->accept();
}
break;
default:
break;
}
}
void WebView::_mouseMoveEvent(QMouseEvent *event)
{
if (mApp->plugins()->processMouseMove(Qz::ON_WebView, this, event)) {
event->accept();
}
}
void WebView::_keyPressEvent(QKeyEvent *event)
{
if (mApp->plugins()->processKeyPress(Qz::ON_WebView, this, event)) {
event->accept();
return;
}
switch (event->key()) {
case Qt::Key_ZoomIn:
zoomIn();
event->accept();
break;
case Qt::Key_ZoomOut:
zoomOut();
event->accept();
break;
case Qt::Key_Plus:
if (event->modifiers() & Qt::ControlModifier) {
zoomIn();
event->accept();
}
break;
case Qt::Key_Minus:
if (event->modifiers() & Qt::ControlModifier) {
zoomOut();
event->accept();
}
break;
case Qt::Key_0:
if (event->modifiers() & Qt::ControlModifier) {
zoomReset();
event->accept();
}
break;
case Qt::Key_Escape:
if (isFullScreen()) {
triggerPageAction(QWebEnginePage::ExitFullScreen);
event->accept();
}
break;
default:
break;
}
}
void WebView::_keyReleaseEvent(QKeyEvent *event)
{
if (mApp->plugins()->processKeyRelease(Qz::ON_WebView, this, event)) {
event->accept();
}
}
void WebView::_contextMenuEvent(QContextMenuEvent *event)
{
Q_UNUSED(event)
}
void WebView::resizeEvent(QResizeEvent *event)
{
QWebEngineView::resizeEvent(event);
emit viewportResized(size());
}
void WebView::contextMenuEvent(QContextMenuEvent *event)
{
// Context menu is created in mouseReleaseEvent
if (s_forceContextMenuOnMouseRelease)
return;
const QPoint pos = event->pos();
const QContextMenuEvent::Reason reason = event->reason();
QTimer::singleShot(0, this, [this, pos, reason]() {
QContextMenuEvent ev(reason, pos);
_contextMenuEvent(&ev);
});
}
void WebView::loadRequest(const LoadRequest &req)
{
if (req.operation() == LoadRequest::GetOperation)
load(req.url());
else
page()->runJavaScript(Scripts::sendPostData(req.url(), req.data()));
}
bool WebView::eventFilter(QObject *obj, QEvent *event)
{
// Hack to find widget that receives input events
if (obj == this && event->type() == QEvent::ChildAdded) {
QWidget *child = qobject_cast<QWidget*>(static_cast<QChildEvent*>(event)->child());
if (child && child->inherits("QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget")) {
m_rwhvqt = child;
m_rwhvqt->installEventFilter(this);
}
}
// Forward events to WebView
if (obj == m_rwhvqt) {
#define HANDLE_EVENT(f, t) \
{ \
bool wasAccepted = event->isAccepted(); \
event->setAccepted(false); \
f(static_cast<t*>(event)); \
bool ret = event->isAccepted(); \
event->setAccepted(wasAccepted); \
return ret; \
}
switch (event->type()) {
case QEvent::KeyPress:
HANDLE_EVENT(_keyPressEvent, QKeyEvent);
case QEvent::KeyRelease:
HANDLE_EVENT(_keyReleaseEvent, QKeyEvent);
case QEvent::MouseButtonPress:
HANDLE_EVENT(_mousePressEvent, QMouseEvent);
case QEvent::MouseButtonRelease:
HANDLE_EVENT(_mouseReleaseEvent, QMouseEvent);
case QEvent::MouseMove:
HANDLE_EVENT(_mouseMoveEvent, QMouseEvent);
case QEvent::Wheel:
HANDLE_EVENT(_wheelEvent, QWheelEvent);
default:
break;
}
#undef HANDLE_EVENT
}
// Block already handled events
if (obj == this) {
switch (event->type()) {
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
case QEvent::Wheel:
return true;
default:
break;
}
}
return QWebEngineView::eventFilter(obj, event);
}