File indexing completed on 2024-05-19 05:01:23

0001 /*
0002     This file is part of the KDE project.
0003 
0004     SPDX-FileCopyrightText: 2007 Trolltech ASA
0005     SPDX-FileCopyrightText: 2008-2010 Urs Wolfer <uwolfer @ kde.org>
0006     SPDX-FileCopyrightText: 2008 Laurent Montel <montel@kde.org>
0007     SPDX-FileCopyrightText: 2009 Dawit Alemayehu <adawit@kde.org>
0008 
0009     SPDX-License-Identifier: LGPL-2.1-or-later
0010 */
0011 
0012 #include "webengineview.h"
0013 #include "webenginepage.h"
0014 #include "webenginepart.h"
0015 #include "webenginepart_ext.h"
0016 #include "settings/webenginesettings.h"
0017 #include "webenginepart_ext.h"
0018 #include "spellcheckermanager.h"
0019 #include "browserinterface.h"
0020 
0021 #include <KIO/Global>
0022 #include <KAboutData>
0023 #include <KActionCollection>
0024 #include <KConfigGroup>
0025 #include <KService>
0026 #include <KUriFilter>
0027 #include <KActionMenu>
0028 #include <KStringHandler>
0029 #include <KLocalizedString>
0030 #include <KIO/CommandLauncherJob>
0031 
0032 #include <QTimer>
0033 #include <QMimeData>
0034 #include <QDropEvent>
0035 #include <QLabel>
0036 #include <QNetworkRequest>
0037 #include <QToolTip>
0038 #include <QCoreApplication>
0039 #include <QMimeType>
0040 #include <QMimeDatabase>
0041 #include <QMenu>
0042 #include <QActionGroup>
0043 
0044 #define QL1S(x)   QLatin1String(x)
0045 #define QL1C(x)   QLatin1Char(x)
0046 
0047 #define ALTERNATE_DEFAULT_WEB_SHORTCUT    QL1S("google")
0048 #define ALTERNATE_WEB_SHORTCUTS           QStringList() << QL1S("google") << QL1S("wikipedia") << QL1S("webster") << QL1S("dmoz")
0049 
0050 WebEngineView::WebEngineView(WebEnginePart* part, QWidget* parent)
0051         :QWebEngineView(parent),
0052          m_actionCollection(new KActionCollection(this)),
0053          m_part(part),
0054          m_autoScrollTimerId(-1),
0055          m_verticalAutoScrollSpeed(0),
0056          m_horizontalAutoScrollSpeed(0),
0057          m_spellCheckMenu(nullptr)
0058 {
0059     setAcceptDrops(true);
0060 
0061     // Create the custom page...
0062     setPage(new WebEnginePage(part, this));
0063 
0064     connect(this, &QWebEngineView::loadStarted, this, &WebEngineView::slotStopAutoScroll);
0065     
0066     if (WebEngineSettings::self()->zoomToDPI())
0067         setZoomFactor(logicalDpiY() / 96.0f);
0068 }
0069 
0070 WebEngineView::~WebEngineView()
0071 {
0072     //qCDebug(WEBENGINEPART_LOG);
0073 }
0074 
0075 void WebEngineView::loadUrl(const QUrl& url, const KParts::OpenUrlArguments& args, const BrowserArguments& bargs)
0076 {
0077     WebEnginePage *pg = qobject_cast<WebEnginePage*>(page());
0078     if (!pg) {
0079         return;
0080     }
0081 
0082     page()->setProperty("NavigationTypeUrlEntered", true);
0083 
0084     if (args.reload() && url == this->url()) {
0085       reload();
0086       return;
0087     }
0088 
0089 //     QNetworkRequest request(url);
0090 //     if (args.reload()) {
0091 //         request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
0092 //     }
0093 
0094     if (bargs.postData.isEmpty()) {
0095         QWebEngineView::load(url);
0096     } else {
0097      //   QWebEngineView::load(url, QNetworkAccessManager::PostOperation, bargs.postData);
0098     }
0099 }
0100 
0101 const QWebEngineContextMenuRequest* WebEngineView::contextMenuResult() const
0102 {
0103     return result();
0104 }
0105 
0106 static void extractMimeTypeFor(const QUrl& url, QString& mimeType)
0107 {
0108     const QString fname(url.fileName());
0109  
0110     if (fname.isEmpty() || url.hasFragment() || url.hasQuery())
0111         return;
0112  
0113     QMimeType pmt = QMimeDatabase().mimeTypeForFile(fname);
0114 
0115     // Further check for mime types guessed from the extension which,
0116     // on a web page, are more likely to be a script delivering content
0117     // of undecidable type. If the mime type from the extension is one
0118     // of these, don't use it.  Retain the original type 'text/html'.
0119     if (pmt.isDefault() ||
0120         pmt.inherits(QL1S("application/x-perl")) ||
0121         pmt.inherits(QL1S("application/x-perl-module")) ||
0122         pmt.inherits(QL1S("application/x-php")) ||
0123         pmt.inherits(QL1S("application/x-python-bytecode")) ||
0124         pmt.inherits(QL1S("application/x-python")) ||
0125         pmt.inherits(QL1S("application/x-shellscript")))
0126         return;
0127 
0128     mimeType = pmt.name();
0129 }
0130 
0131 void WebEngineView::contextMenuEvent(QContextMenuEvent* e)
0132 {
0133 #if QT_VERSION_MAJOR < 6
0134     m_result = page()->contextMenuData();
0135 #else
0136     m_result = lastContextMenuRequest();
0137 #endif
0138 
0139     // Clear the previous collection entries first...
0140     m_actionCollection->clear();
0141 
0142     KParts::NavigationExtension::PopupFlags flags = KParts::NavigationExtension::DefaultPopupItems;
0143     KParts::NavigationExtension::ActionGroupMap mapAction;
0144     QString mimeType (QL1S("text/html"));
0145     bool forcesNewWindow = false;
0146 
0147     QUrl emitUrl;
0148 
0149     if (result()->isContentEditable()) {
0150         flags |= KParts::NavigationExtension::ShowTextSelectionItems;
0151         editableContentActionPopupMenu(mapAction);
0152     } else if (result()->mediaType() == QWebEngineContextMenuRequest::MediaTypeVideo || result()->mediaType() == QWebEngineContextMenuRequest::MediaTypeAudio) {
0153         multimediaActionPopupMenu(mapAction);
0154     } else if (!result()->linkUrl().isValid()) {
0155         if (result()->mediaType() == QWebEngineContextMenuRequest::MediaTypeImage) {
0156             emitUrl = result()->mediaUrl();
0157             extractMimeTypeFor(emitUrl, mimeType);
0158         } else {
0159             flags |= KParts::NavigationExtension::ShowBookmark;
0160             emitUrl = m_part->url();
0161 
0162             if (!result()->selectedText().isEmpty()) {
0163                 flags |= KParts::NavigationExtension::ShowTextSelectionItems;
0164                 selectActionPopupMenu(mapAction);
0165             }
0166         }
0167         partActionPopupMenu(mapAction);
0168     } else {
0169         flags |= KParts::NavigationExtension::ShowBookmark;
0170         flags |= KParts::NavigationExtension::IsLink;
0171         emitUrl = result()->linkUrl();
0172         linkActionPopupMenu(mapAction);
0173         if (emitUrl.isLocalFile())
0174             mimeType = QMimeDatabase().mimeTypeForUrl(emitUrl).name();
0175         else
0176             extractMimeTypeFor(emitUrl, mimeType);
0177         partActionPopupMenu(mapAction);
0178 
0179         // Show the OpenInThisWindow context menu item
0180 //        forcesNewWindow = (page()->currentFrame() != result()->linkTargetFrame());
0181     }
0182 
0183     if (!mapAction.isEmpty()) {
0184         KParts::OpenUrlArguments args;
0185         BrowserArguments bargs;
0186         args.setMimeType(mimeType);
0187         bargs.setForcesNewWindow(forcesNewWindow);
0188         e->accept();
0189 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0190         emit m_part->browserExtension()->popupMenu(e->globalPos(), emitUrl, static_cast<mode_t>(-1), args, bargs, flags, mapAction);
0191 #else
0192         emit m_part->browserExtension()->browserPopupMenuFromUrl(e->globalPos(), emitUrl, static_cast<mode_t>(-1), args, bargs, flags, mapAction);
0193 #endif
0194         return;
0195     }
0196 
0197     QWebEngineView::contextMenuEvent(e);
0198 }
0199 
0200 void WebEngineView::keyPressEvent(QKeyEvent* e)
0201 {
0202     if (e && hasFocus()) {
0203         const int key = e->key();
0204         if (e->modifiers() & Qt::ShiftModifier) {
0205             switch (key) {
0206             case Qt::Key_Up:
0207                /* if (!isEditableElement(page()))*/ {
0208                     m_verticalAutoScrollSpeed--;
0209                     if (m_autoScrollTimerId == -1)
0210                         m_autoScrollTimerId = startTimer(100);
0211                     e->accept();
0212                     return;
0213                 }
0214                 break;
0215             case Qt::Key_Down:
0216                 /*if (!isEditableElement(page()))*/ {
0217                     m_verticalAutoScrollSpeed++;
0218                     if (m_autoScrollTimerId == -1)
0219                         m_autoScrollTimerId = startTimer(100);
0220                     e->accept();
0221                     return;
0222                 }
0223                 break;
0224             case Qt::Key_Left:
0225                 /*if (!isEditableElement(page()))*/ {
0226                     m_horizontalAutoScrollSpeed--;
0227                     if (m_autoScrollTimerId == -1)
0228                         m_autoScrollTimerId = startTimer(100);
0229                     e->accept();
0230                     return;
0231                 }
0232                 break;
0233             case Qt::Key_Right:
0234                 /*if (!isEditableElement(page()))*/ {
0235                     m_horizontalAutoScrollSpeed--;
0236                     if (m_autoScrollTimerId == -1)
0237                         m_autoScrollTimerId = startTimer(100);
0238                     e->accept();
0239                     return;
0240                 }
0241                 break;
0242             default:
0243                 break;
0244             }
0245         } else if (m_autoScrollTimerId != -1) {
0246             // qCDebug(WEBENGINEPART_LOG) << "scroll timer id:" << m_autoScrollTimerId;
0247             slotStopAutoScroll();
0248             e->accept();
0249             return;
0250         }
0251     }
0252     QWebEngineView::keyPressEvent(e);
0253 }
0254 
0255 void WebEngineView::keyReleaseEvent(QKeyEvent *e)
0256 {
0257     QWebEngineView::keyReleaseEvent(e);
0258 }
0259 
0260 void WebEngineView::mouseReleaseEvent(QMouseEvent* e)
0261 {
0262     QWebEngineView::mouseReleaseEvent(e);
0263 }
0264 
0265 void WebEngineView::wheelEvent (QWheelEvent* e)
0266 {
0267     QWebEngineView::wheelEvent(e);
0268 }
0269 
0270 
0271 void WebEngineView::timerEvent(QTimerEvent* e)
0272 {
0273 #if 0
0274     if (e && e->timerId() == m_autoScrollTimerId) {
0275         // do the scrolling
0276         scroll(m_horizontalAutoScrollSpeed, m_verticalAutoScrollSpeed);
0277         // check if we reached the end
0278         const int y = page()->scrollPosition().y();
0279         if (y == page()->currentFrame()->scrollBarMinimum(Qt::Vertical) ||
0280             y == page()->currentFrame()->scrollBarMaximum(Qt::Vertical)) {
0281             m_verticalAutoScrollSpeed = 0;
0282         }
0283 
0284         const int x = page()->scrollPosition().x();
0285         if (x == page()->currentFrame()->scrollBarMinimum(Qt::Horizontal) ||
0286             x == page()->currentFrame()->scrollBarMaximum(Qt::Horizontal)) {
0287             m_horizontalAutoScrollSpeed = 0;
0288         }
0289 
0290         // Kill the timer once the max/min scroll limit is reached.
0291         if (m_horizontalAutoScrollSpeed == 0  && m_verticalAutoScrollSpeed == 0) {
0292             killTimer(m_autoScrollTimerId);
0293             m_autoScrollTimerId = -1;
0294         }
0295         e->accept();
0296         return;
0297     }
0298 #endif
0299     QWebEngineView::timerEvent(e);
0300 }
0301 
0302 void WebEngineView::editableContentActionPopupMenu(KParts::NavigationExtension::ActionGroupMap& partGroupMap)
0303 {
0304     if (m_spellCheckMenu) {
0305         m_spellCheckMenu->deleteLater();
0306         m_spellCheckMenu = nullptr;
0307     }
0308     QList<QAction*> editableContentActions;
0309 
0310     QActionGroup* group = new QActionGroup(this);
0311     group->setExclusive(true);
0312 
0313     QAction* action = new QAction(m_actionCollection);
0314     action->setSeparator(true);
0315     editableContentActions.append(action);
0316 
0317     WebEngineNavigationExtension *ext = qobject_cast<WebEngineNavigationExtension *>(m_part->navigationExtension());
0318     Q_ASSERT(ext!=nullptr);
0319 
0320     action = KStandardAction::create(KStandardAction::Copy, ext, &WebEngineNavigationExtension::copy, m_actionCollection);
0321     action->setEnabled(pageAction(QWebEnginePage::Copy)->isEnabled());
0322     editableContentActions.append(action);
0323 
0324     action = KStandardAction::create(KStandardAction::Cut, ext, &WebEngineNavigationExtension::cut, m_actionCollection);
0325     action->setEnabled(pageAction(QWebEnginePage::Cut)->isEnabled());
0326     editableContentActions.append(action);
0327 
0328     action = KStandardAction::create(KStandardAction::Paste, ext, &WebEngineNavigationExtension::paste, m_actionCollection);
0329     action->setEnabled(pageAction(QWebEnginePage::Paste)->isEnabled());
0330     editableContentActions.append(action);
0331 
0332     action = new QAction(m_actionCollection);
0333     action->setSeparator(true);
0334     editableContentActions.append(action);
0335 
0336     editableContentActions.append(pageAction(QWebEnginePage::SelectAll));
0337     editableContentActions.append(pageAction(QWebEnginePage::InspectElement));
0338 
0339 
0340 #if QT_VERSION_MAJOR < 6
0341     const QWebEngineContextMenuRequest *req = &page()->contextMenuData();
0342 #else
0343     QWebEngineContextMenuRequest *req = lastContextMenuRequest();
0344 #endif
0345     SpellCheckerManager *manager = m_part->spellCheckerManager();
0346     m_spellCheckMenu = manager->spellCheckingMenu(req->spellCheckerSuggestions(), m_actionCollection, dynamic_cast<WebEnginePage*>(page()));
0347     if (m_spellCheckMenu) {
0348         editableContentActions.append(m_spellCheckMenu->menuAction());
0349     }
0350 
0351     partGroupMap.insert(QStringLiteral("editactions") , editableContentActions);
0352 }
0353 
0354 void WebEngineView::partActionPopupMenu(KParts::NavigationExtension::ActionGroupMap& partGroupMap)
0355 {
0356     QList<QAction*> partActions;
0357 
0358     WebEngineNavigationExtension *ext = qobject_cast<WebEngineNavigationExtension *>(m_part->navigationExtension());
0359     Q_ASSERT(ext!=nullptr);
0360     if (result()->mediaUrl().isValid()) {
0361         QAction *action;
0362         action = new QAction(i18n("Save Image As..."), this);
0363         m_actionCollection->addAction(QL1S("saveimageas"), action);
0364         connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotSaveImageAs);
0365         partActions.append(action);
0366 
0367         action = new QAction(i18n("Send Image..."), this);
0368         m_actionCollection->addAction(QL1S("sendimage"), action);
0369         connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotSendImage);
0370         partActions.append(action);
0371 
0372         action = new QAction(i18n("Copy Image URL"), this);
0373         m_actionCollection->addAction(QL1S("copyimageurl"), action);
0374         connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotCopyImageURL);
0375         partActions.append(action);
0376 
0377 #if 0
0378         action = new QAction(i18n("Copy Image"), this);
0379         m_actionCollection->addAction(QL1S("copyimage"), action);
0380         connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotCopyImage);
0381         action->setEnabled(!result()->pixmap().isNull());
0382         partActions.append(action);
0383 #endif
0384 
0385         action = new QAction(i18n("View Image (%1)", QUrl(result()->mediaUrl()).fileName()), this);
0386         m_actionCollection->addAction(QL1S("viewimage"), action);
0387         connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotViewImage);
0388         partActions.append(action);
0389 
0390         if (WebEngineSettings::self()->isAdFilterEnabled()) {
0391             action = new QAction(i18n("Block Image..."), this);
0392             m_actionCollection->addAction(QL1S("blockimage"), action);
0393             connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotBlockImage);
0394             partActions.append(action);
0395 
0396             if (!result()->mediaUrl().host().isEmpty() &&
0397                 !result()->mediaUrl().scheme().isEmpty())
0398             {
0399                 action = new QAction(i18n("Block Images From %1" , result()->mediaUrl().host()), this);
0400                 m_actionCollection->addAction(QL1S("blockhost"), action);
0401                 connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotBlockHost);
0402                 partActions.append(action);
0403             }
0404         }
0405     }
0406 
0407     {
0408         QAction *separatorAction = new QAction(m_actionCollection);
0409         separatorAction->setSeparator(true);
0410         partActions.append(separatorAction);
0411     }
0412 
0413     partActions.append(m_part->actionCollection()->action(QStringLiteral("viewDocumentSource")));
0414 
0415     partActions.append(pageAction(QWebEnginePage::InspectElement));
0416 
0417     partGroupMap.insert(QStringLiteral("partactions"), partActions);
0418 }
0419 
0420 void WebEngineView::selectActionPopupMenu(KParts::NavigationExtension::ActionGroupMap& selectGroupMap)
0421 {
0422     QList<QAction*> selectActions;
0423 
0424     WebEngineNavigationExtension *ext = qobject_cast<WebEngineNavigationExtension *>(m_part->navigationExtension());
0425     Q_ASSERT(ext!=nullptr);
0426 
0427     QAction* copyAction = KStandardAction::create(KStandardAction::Copy,
0428                                                   ext, &WebEngineNavigationExtension::copy, m_actionCollection);
0429     copyAction->setText(i18n("&Copy Text"));
0430     copyAction->setEnabled(ext->isActionEnabled("copy"));
0431     selectActions.append(copyAction);
0432 
0433     addSearchActions(selectActions, this);
0434 
0435     KUriFilterData data (selectedText().simplified().left(256));
0436     data.setCheckForExecutables(false);
0437     if (KUriFilter::self()->filterUri(data, QStringList() << QStringLiteral("kshorturifilter") << QStringLiteral("fixhosturifilter")) &&
0438         data.uri().isValid() && data.uriType() == KUriFilterData::NetProtocol) {
0439         QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("open selected url", "Open '%1'",
0440                                             KStringHandler::rsqueeze(data.uri().url(), 18)), this);
0441         m_actionCollection->addAction(QL1S("openSelection"), action);
0442         action->setData(QUrl(data.uri()));
0443         connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotOpenSelection);
0444         selectActions.append(action);
0445     }
0446 
0447     selectGroupMap.insert(QStringLiteral("editactions"), selectActions);
0448 }
0449 
0450 void WebEngineView::linkActionPopupMenu(KParts::NavigationExtension::ActionGroupMap& linkGroupMap)
0451 {
0452     Q_ASSERT(!result()->linkUrl().isEmpty());
0453 
0454     const QUrl url(result()->linkUrl());
0455 
0456     QList<QAction*> linkActions;
0457 
0458     QAction* action;
0459 
0460     WebEngineNavigationExtension *ext = qobject_cast<WebEngineNavigationExtension *>(m_part->navigationExtension());
0461     Q_ASSERT(ext!=nullptr);
0462 
0463     if (!result()->selectedText().isEmpty()) {
0464         action = KStandardAction::create(KStandardAction::Copy, ext, &WebEngineNavigationExtension::copy,
0465                                          m_actionCollection);
0466         action->setText(i18n("&Copy Text"));
0467         action->setEnabled(ext->isActionEnabled("copy"));
0468         linkActions.append(action);
0469     }
0470 
0471     if (url.scheme() == QLatin1String("mailto")) {
0472         action = new QAction(i18n("&Copy Email Address"), this);
0473         m_actionCollection->addAction(QL1S("copylinklocation"), action);
0474         connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotCopyEmailAddress);
0475         linkActions.append(action);
0476     } else {
0477         if (!result()->linkText().isEmpty()) {
0478             action = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy Link &Text"), this);
0479             m_actionCollection->addAction(QL1S("copylinktext"), action);
0480             connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotCopyLinkText);
0481             linkActions.append(action);
0482         }
0483 
0484         action = new QAction(i18n("Copy Link &URL"), this);
0485         m_actionCollection->addAction(QL1S("copylinkurl"), action);
0486         connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotCopyLinkURL);
0487         linkActions.append(action);
0488 
0489         action = new QAction(i18n("&Save Link As..."), this);
0490         m_actionCollection->addAction(QL1S("savelinkas"), action);
0491         auto saveLinkAsLambda = [this, url](bool){qobject_cast<WebEngineNavigationExtension*>(m_part->navigationExtension())->slotSaveLinkAs(url);};
0492         connect(action, &QAction::triggered, m_part->navigationExtension(), saveLinkAsLambda);
0493         linkActions.append(action);
0494     }
0495 
0496     linkGroupMap.insert(QStringLiteral("linkactions"), linkActions);
0497 }
0498 
0499 void WebEngineView::multimediaActionPopupMenu(KParts::NavigationExtension::ActionGroupMap& mmGroupMap)
0500 {
0501     QList<QAction*> multimediaActions;
0502 
0503     const bool isVideoElement = result()->mediaType() == QWebEngineContextMenuRequest::MediaTypeVideo;
0504     const bool isAudioElement = result()->mediaType() == QWebEngineContextMenuRequest::MediaTypeAudio;
0505 
0506     WebEngineNavigationExtension *ext = qobject_cast<WebEngineNavigationExtension *>(m_part->navigationExtension());
0507     Q_ASSERT(ext!=nullptr);
0508 
0509     QAction* action = new QAction(i18n("&Play/Pause"), this);
0510     m_actionCollection->addAction(QL1S("playmultimedia"), action);
0511     connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotPlayMedia);
0512     multimediaActions.append(action);
0513 
0514     action = new QAction(i18n("Un&mute/&Mute"), this);
0515     m_actionCollection->addAction(QL1S("mutemultimedia"), action);
0516     connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotMuteMedia);
0517     multimediaActions.append(action);
0518 
0519     action = new QAction(i18n("Toggle &Loop"), this);
0520     m_actionCollection->addAction(QL1S("loopmultimedia"), action);
0521     connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotLoopMedia);
0522     multimediaActions.append(action);
0523 
0524     action = new QAction(i18n("Toggle &Controls"), this);
0525     m_actionCollection->addAction(QL1S("showmultimediacontrols"), action);
0526     connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotShowMediaControls);
0527     multimediaActions.append(action);
0528 
0529     action = new QAction(m_actionCollection);
0530     action->setSeparator(true);
0531     multimediaActions.append(action);
0532 
0533     QString saveMediaText, copyMediaText;
0534     if (isVideoElement) {
0535         saveMediaText = i18n("Sa&ve Video As...");
0536         copyMediaText = i18n("C&opy Video URL");
0537     } else if (isAudioElement) {
0538         saveMediaText = i18n("Sa&ve Audio As...");
0539         copyMediaText = i18n("C&opy Audio URL");
0540     } else {
0541         saveMediaText = i18n("Sa&ve Media As...");
0542         copyMediaText = i18n("C&opy Media URL");
0543     }
0544 
0545     action = new QAction(saveMediaText, this);
0546     m_actionCollection->addAction(QL1S("savemultimedia"), action);
0547     connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotSaveMedia);
0548     multimediaActions.append(action);
0549 
0550     action = new QAction(copyMediaText, this);
0551     m_actionCollection->addAction(QL1S("copymultimediaurl"), action);
0552     connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::slotCopyMedia);
0553     multimediaActions.append(action);
0554 
0555     mmGroupMap.insert(QStringLiteral("partactions"), multimediaActions);
0556 }
0557 
0558 void WebEngineView::slotConfigureWebShortcuts()
0559 {
0560     auto job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell5"), {QStringLiteral("webshortcuts")});
0561     job->start();
0562 }
0563 
0564 void WebEngineView::slotStopAutoScroll()
0565 {
0566     if (m_autoScrollTimerId == -1) {
0567         return;
0568     }
0569 
0570     killTimer(m_autoScrollTimerId);
0571     m_autoScrollTimerId = -1;
0572     m_verticalAutoScrollSpeed = 0;
0573     m_horizontalAutoScrollSpeed = 0;
0574 
0575 }
0576 
0577 void WebEngineView::addSearchActions(QList<QAction*>& selectActions, QWebEngineView* view)
0578 {
0579    // search text
0580     const QString selectedText = view->selectedText().simplified();
0581     if (selectedText.isEmpty())
0582         return;
0583 
0584     WebEngineNavigationExtension *ext = qobject_cast<WebEngineNavigationExtension *>(m_part->navigationExtension());
0585     Q_ASSERT(ext!=nullptr);
0586 
0587     KUriFilterData data;
0588     data.setData(selectedText);
0589     data.setAlternateDefaultSearchProvider(ALTERNATE_DEFAULT_WEB_SHORTCUT);
0590     data.setAlternateSearchProviders(ALTERNATE_WEB_SHORTCUTS);
0591 
0592     if (KUriFilter::self()->filterSearchUri(data, KUriFilter::NormalTextFilter)) {
0593         const QString squeezedText = KStringHandler::rsqueeze(selectedText, 20);
0594         QAction *action = new QAction(QIcon::fromTheme(data.iconName()),
0595                                       i18nc("Search \"search provider\" for \"text\"", "Search %1 for '%2'",
0596                                             data.searchProvider(), squeezedText), view);
0597         action->setData(QUrl(data.uri()));
0598         connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::searchProvider);
0599         m_actionCollection->addAction(QL1S("defaultSearchProvider"), action);
0600         selectActions.append(action);
0601 
0602 
0603         const QStringList preferredSearchProviders = data.preferredSearchProviders();
0604         if (!preferredSearchProviders.isEmpty()) {
0605             KActionMenu* providerList = new KActionMenu(i18nc("Search for \"text\" with",
0606                                                               "Search for '%1' with", squeezedText), view);
0607             for (const QString &searchProvider: preferredSearchProviders) {
0608                 if (searchProvider == data.searchProvider())
0609                     continue;
0610 
0611                 QAction *action = new QAction(QIcon::fromTheme(data.iconNameForPreferredSearchProvider(searchProvider)), searchProvider, view);
0612                 action->setData(data.queryForPreferredSearchProvider(searchProvider));
0613                 m_actionCollection->addAction(searchProvider, action);
0614                 connect(action, &QAction::triggered, ext, &WebEngineNavigationExtension::searchProvider);
0615 
0616                 providerList->addAction(action);
0617             }
0618 
0619             QAction *action = new QAction(i18n("Configure Web Shortcuts..."), view);
0620             action->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
0621             connect(action, &QAction::triggered, this, &WebEngineView::slotConfigureWebShortcuts);
0622             providerList->addAction(action);
0623 
0624             m_actionCollection->addAction(QL1S("searchProviderList"), providerList);
0625             selectActions.append(providerList);
0626         }
0627     }
0628 }
0629 
0630 #ifndef REMOTE_DND_NOT_HANDLED_BY_WEBENGINE
0631 void WebEngineView::dropEvent(QDropEvent* e)
0632 {
0633     WebEnginePage *pg = qobject_cast<WebEnginePage*>(page());
0634     if (pg) {
0635         pg->setDropOperationStarted();
0636     }
0637     QWebEngineView::dropEvent(e);
0638 }
0639 
0640 #else
0641 void WebEngineView::dropEvent(QDropEvent* e)
0642 {
0643     QWebEngineView::dropEvent(e);
0644     //Unlike in acceptProposedDragEventIfPossible, we don't check !e->isAccepted because it seems that it's always true
0645     //(if the move event was accepted, this is accepted automatically; if the move event was rejected, this function
0646     //isn't called at all)
0647     if (!m_dragAndDropHandledBySuperclass && e->mimeData()->hasUrls()) {
0648         m_dragAndDropHandledBySuperclass = true;
0649         emit m_part->navigationExtension()->openUrlRequest(e->mimeData()->urls().first());
0650         e->acceptProposedAction();
0651     }
0652 }
0653 
0654 void WebEngineView::acceptDragMoveEventIfPossible(QDragMoveEvent* e)
0655 {
0656     if (!e->isAccepted() && e->mimeData()->hasUrls()) {
0657         e->acceptProposedAction();
0658         m_dragAndDropHandledBySuperclass = false;
0659     } else {
0660         m_dragAndDropHandledBySuperclass = true;
0661     }
0662 }
0663 
0664 void WebEngineView::dragEnterEvent(QDragEnterEvent* e)
0665 {
0666     QWebEngineView::dragEnterEvent(e);
0667     acceptDragMoveEventIfPossible(e);
0668 }
0669 
0670 void WebEngineView::dragMoveEvent(QDragMoveEvent* e)
0671 {
0672     QWebEngineView::dragMoveEvent(e);
0673     acceptDragMoveEventIfPossible(e);
0674 }
0675 #endif
0676 
0677 QWebEngineContextMenuRequest* WebEngineView::result()
0678 {
0679 #if QT_VERSION_MAJOR < 6
0680     return &m_result;
0681 #else
0682     return m_result;
0683 #endif
0684 }
0685 
0686 const QWebEngineContextMenuRequest* WebEngineView::result() const
0687 {
0688 #if QT_VERSION_MAJOR < 6
0689     return &m_result;
0690 #else
0691     return m_result;
0692 #endif
0693 }