1 /*
2    SPDX-FileCopyrightText: 2016-2021 Laurent Montel <montel@kde.org>
3 
4    SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "akrwebengineviewer.h"
8 #include "articleviewer-ng/webengine/articleviewerwebenginepage.h"
9 
10 #include "actionmanager.h"
11 #include "actions.h"
12 #include "akregatorconfig.h"
13 #include "webengine/urlhandlerwebenginemanager.h"
14 
15 #include <KActionCollection>
16 #include <KActionMenu>
17 #include <KIO/KUriFilterSearchProviderActions>
18 #include <KPIMTextEdit/TextToSpeech>
19 #include <QContextMenuEvent>
20 #include <QMenu>
21 #include <QWebEngineHistory>
22 #include <QWebEngineSettings>
23 #include <WebEngineViewer/BlockExternalResourcesUrlInterceptor>
24 #include <WebEngineViewer/InterceptorManager>
25 #include <WebEngineViewer/LoadExternalReferencesUrlInterceptor>
26 #include <WebEngineViewer/WebHitTest>
27 #include <WebEngineViewer/WebHitTestResult>
28 
29 using namespace Akregator;
30 
AkrWebEngineViewer(KActionCollection * ac,QWidget * parent)31 AkrWebEngineViewer::AkrWebEngineViewer(KActionCollection *ac, QWidget *parent)
32     : ArticleViewerWebEngine(ac, parent)
33 {
34     if (mExternalReference) {
35         mExternalReference->setAllowExternalContent(true);
36     }
37     if (mBlockExternalReference) { // Don't block element
38         mBlockExternalReference->setEnabled(false);
39     }
40     // TODO update settings when we change config
41     settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::enableJavascript());
42     settings()->setAttribute(QWebEngineSettings::PluginsEnabled, false);
43     settings()->setAttribute(QWebEngineSettings::AutoLoadImages, true);
44     settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
45     settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, false);
46     settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
47     settings()->setAttribute(QWebEngineSettings::XSSAuditingEnabled, false);
48     settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false);
49     settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, false);
50     // settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, false);
51 
52     settings()->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, false);
53 }
54 
~AkrWebEngineViewer()55 AkrWebEngineViewer::~AkrWebEngineViewer()
56 {
57 }
58 
updateSecurity()59 void AkrWebEngineViewer::updateSecurity()
60 {
61     // Nothing
62 }
63 
contextMenuEvent(QContextMenuEvent * e)64 void AkrWebEngineViewer::contextMenuEvent(QContextMenuEvent *e)
65 {
66     displayContextMenu(e->pos());
67 }
68 
slotWebHitFinished(const WebEngineViewer::WebHitTestResult & result)69 void AkrWebEngineViewer::slotWebHitFinished(const WebEngineViewer::WebHitTestResult &result)
70 {
71     mCurrentUrl = result.linkUrl();
72     if (URLHandlerWebEngineManager::instance()->handleContextMenuRequest(mCurrentUrl, mapToGlobal(result.pos()), this)) {
73         return;
74     }
75     QMenu popup(this);
76     QWebEngineHistory *history = page()->history();
77     bool needSeparator = false;
78     if (history->canGoBack()) {
79         popup.addAction(pageAction(QWebEnginePage::Back));
80         needSeparator = true;
81     }
82 
83     if (history->canGoForward()) {
84         popup.addAction(pageAction(QWebEnginePage::Forward));
85         needSeparator = true;
86     }
87     if (needSeparator) {
88         popup.addSeparator();
89     }
90     popup.addAction(pageAction(QWebEnginePage::Stop));
91     popup.addSeparator();
92     popup.addAction(pageAction(QWebEnginePage::Reload));
93     popup.addSeparator();
94 
95     const bool noContentSelected = selectedText().isEmpty();
96     if (noContentSelected) {
97         if (!mCurrentUrl.isEmpty()) {
98             {
99                 QAction *act = createOpenLinkInNewTabAction(mCurrentUrl, &popup);
100                 connect(act, &QAction::triggered, this, &AkrWebEngineViewer::slotOpenLinkInBackgroundTab);
101                 popup.addAction(act);
102             }
103             {
104                 QAction *act = createOpenLinkInExternalBrowserAction(mCurrentUrl, &popup);
105                 connect(act, &QAction::triggered, this, &AkrWebEngineViewer::slotOpenLinkInBrowser);
106                 popup.addAction(act);
107             }
108             popup.addSeparator();
109             popup.addAction(mActionCollection->action(QStringLiteral("savelinkas")));
110             popup.addAction(mActionCollection->action(QStringLiteral("copylinkaddress")));
111         }
112         if (!result.imageUrl().isEmpty()) {
113             popup.addSeparator();
114             popup.addAction(mActionCollection->action(QStringLiteral("copy_image_location")));
115             popup.addAction(mActionCollection->action(QStringLiteral("saveas_imageurl")));
116         }
117         popup.addSeparator();
118         popup.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl));
119         popup.addSeparator();
120         popup.addAction(mShareServiceManager->menu());
121     } else {
122         popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_copy")));
123         popup.addSeparator();
124         mWebShortcutMenuManager->setSelectedText(page()->selectedText());
125         mWebShortcutMenuManager->addWebShortcutsToMenu(&popup);
126         popup.addSeparator();
127         popup.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection));
128     }
129     popup.addSeparator();
130     popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_print")));
131     popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_printpreview")));
132     popup.addSeparator();
133     popup.addAction(pageAction(QWebEnginePage::SavePage));
134     popup.addSeparator();
135     popup.addAction(ActionManager::getInstance()->action(QStringLiteral("tab_mute")));
136     popup.addAction(ActionManager::getInstance()->action(QStringLiteral("tab_unmute")));
137     const QList<QAction *> interceptorUrlActions = mNetworkAccessManager->interceptorUrlActions(result);
138     if (!interceptorUrlActions.isEmpty()) {
139         popup.addSeparator();
140         popup.addActions(interceptorUrlActions);
141     }
142     popup.addSeparator();
143     popup.addAction(ActionManager::getInstance()->action(QStringLiteral("find_in_messages")));
144     if (KPIMTextEdit::TextToSpeech::self()->isReady()) {
145         popup.addSeparator();
146         popup.addAction(ActionManager::getInstance()->action(QStringLiteral("speak_text")));
147     }
148     popup.exec(mapToGlobal(result.pos()));
149 }
150 
displayContextMenu(const QPoint & pos)151 void AkrWebEngineViewer::displayContextMenu(const QPoint &pos)
152 {
153     WebEngineViewer::WebHitTest *webHit = mPageEngine->hitTestContent(pos);
154     connect(webHit, &WebEngineViewer::WebHitTest::finished, this, &AkrWebEngineViewer::slotWebHitFinished);
155 }
156 
createWindow(QWebEnginePage::WebWindowType type)157 QWebEngineView *AkrWebEngineViewer::createWindow(QWebEnginePage::WebWindowType type)
158 {
159     Q_UNUSED(type)
160     return this;
161 }
162