Messagelib

mailwebengineview.cpp
1/*
2 SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6#include "mailwebengineview.h"
7#include "../urlhandlermanager.h"
8#include "cidreferencesurlinterceptor/cidreferencesurlinterceptor.h"
9#include "cidschemehandler/cidschemehandler.h"
10#include "loadexternalreferencesurlinterceptor/loadexternalreferencesurlinterceptor.h"
11#include "mailwebenginepage.h"
12#include "messageviewer/messageviewersettings.h"
13#include "webengineviewer/webengineaccesskey.h"
14#include "webengineviewer/webenginescript.h"
15#include <WebEngineViewer/BlockExternalResourcesUrlInterceptor>
16#include <WebEngineViewer/InterceptorManager>
17#include <WebEngineViewer/WebEngineManageScript>
18
19#include "scamdetection/scamdetectionwebengine.h"
20#include <QContextMenuEvent>
21#include <QWebEngineProfile>
22#include <WebEngineViewer/WebHitTest>
23
24#include <QPainter>
25#include <QWebEngineUrlScheme>
26
27#include <WebEngineViewer/WebHitTestResult>
28
29using namespace MessageViewer;
30template<typename Arg, typename R, typename C>
31struct InvokeWrapper {
32 R *receiver;
33 void (C::*memberFunction)(Arg);
34 void operator()(Arg result)
35 {
36 (receiver->*memberFunction)(result);
37 }
38};
39
40template<typename Arg, typename R, typename C>
41
42InvokeWrapper<Arg, R, C> invoke(R *receiver, void (C::*memberFunction)(Arg))
43{
44 InvokeWrapper<Arg, R, C> wrapper = {receiver, memberFunction};
45 return wrapper;
46}
47
48class MessageViewer::MailWebEngineViewPrivate
49{
50public:
51 MailWebEngineViewPrivate() = default;
52
53 QUrl mHoveredUrl;
54 QPoint mLastClickPosition;
55 ScamDetectionWebEngine *mScamDetection = nullptr;
56 WebEngineViewer::WebEngineAccessKey *mWebViewAccessKey = nullptr;
57 MessageViewer::LoadExternalReferencesUrlInterceptor *mExternalReference = nullptr;
58 MailWebEnginePage *mPageEngine = nullptr;
59 WebEngineViewer::InterceptorManager *mNetworkAccessManager = nullptr;
60 MessageViewer::ViewerPrivate *mViewer = nullptr;
61 WebEngineViewer::BlockTrackingUrlInterceptor *mBlockMailTrackingUrl = nullptr;
62 bool mCanStartDrag = false;
63};
64
65MailWebEngineView::MailWebEngineView(KActionCollection *ac, QWidget *parent)
66 : WebEngineViewer::WebEngineView(parent)
67 , d(new MessageViewer::MailWebEngineViewPrivate)
68{
69 d->mPageEngine = new MailWebEnginePage(this);
70 setPage(d->mPageEngine);
71 d->mWebViewAccessKey = new WebEngineViewer::WebEngineAccessKey(this, this);
72 d->mWebViewAccessKey->setActionCollection(ac);
73 d->mScamDetection = new ScamDetectionWebEngine(this);
74 connect(d->mScamDetection, &ScamDetectionWebEngine::messageMayBeAScam, this, &MailWebEngineView::messageMayBeAScam);
75 connect(d->mWebViewAccessKey, &WebEngineViewer::WebEngineAccessKey::openUrl, this, &MailWebEngineView::openUrl);
76 connect(this, &MailWebEngineView::loadFinished, this, &MailWebEngineView::slotLoadFinished);
77
78 d->mPageEngine->profile()->installUrlSchemeHandler(QByteArrayLiteral("cid"), new CidSchemeHandler(this));
79
80 d->mNetworkAccessManager = new WebEngineViewer::InterceptorManager(this, ac, this);
81 d->mExternalReference = new MessageViewer::LoadExternalReferencesUrlInterceptor(this);
82 connect(d->mExternalReference, &MessageViewer::LoadExternalReferencesUrlInterceptor::urlBlocked, this, &MailWebEngineView::urlBlocked);
83 d->mNetworkAccessManager->addInterceptor(d->mExternalReference);
84 auto cidReference = new MessageViewer::CidReferencesUrlInterceptor(this);
85 d->mNetworkAccessManager->addInterceptor(cidReference);
86 auto blockExternalUrl = new WebEngineViewer::BlockExternalResourcesUrlInterceptor(this);
87 connect(blockExternalUrl, &WebEngineViewer::BlockExternalResourcesUrlInterceptor::formSubmittedForbidden, this, &MailWebEngineView::formSubmittedForbidden);
88 d->mNetworkAccessManager->addInterceptor(blockExternalUrl);
89
90 d->mBlockMailTrackingUrl = new WebEngineViewer::BlockTrackingUrlInterceptor(this);
91 connect(d->mBlockMailTrackingUrl, &WebEngineViewer::BlockTrackingUrlInterceptor::trackingFound, this, &MailWebEngineView::mailTrackingFound);
92 d->mNetworkAccessManager->addInterceptor(d->mBlockMailTrackingUrl);
93
94 setFocusPolicy(Qt::WheelFocus);
95 connect(d->mPageEngine, &MailWebEnginePage::urlClicked, this, &MailWebEngineView::openUrl);
96 connect(page(), &QWebEnginePage::scrollPositionChanged, d->mWebViewAccessKey, &WebEngineViewer::WebEngineAccessKey::hideAccessKeys);
97}
98
99MailWebEngineView::~MailWebEngineView() = default;
100
101void MailWebEngineView::readConfig()
102{
103 d->mBlockMailTrackingUrl->setEnabledMailTrackingInterceptor(MessageViewer::MessageViewerSettings::self()->mailTrackingUrlEnabled());
104}
105
106void MailWebEngineView::setLinkHovered(const QUrl &url)
107{
108 // TODO we need to detect image url too.
109 d->mHoveredUrl = url;
110}
111
112void MailWebEngineView::runJavaScriptInWordId(const QString &script)
113{
114 page()->runJavaScript(script, WebEngineViewer::WebEngineManageScript::scriptWordId());
115}
116
117void MailWebEngineView::setViewer(MessageViewer::ViewerPrivate *viewer)
118{
119 d->mViewer = viewer;
120}
121
122void MailWebEngineView::contextMenuEvent(QContextMenuEvent *e)
123{
124 WebEngineViewer::WebHitTest *webHit = d->mPageEngine->hitTestContent(e->pos());
125 connect(webHit, &WebEngineViewer::WebHitTest::finished, this, &MailWebEngineView::slotWebHitFinished);
126}
127
128void MailWebEngineView::slotWebHitFinished(const WebEngineViewer::WebHitTestResult &result)
129{
130 Q_EMIT popupMenu(result);
131}
132
133void MailWebEngineView::scrollUp(int pixels)
134{
135 runJavaScriptInWordId(WebEngineViewer::WebEngineScript::scrollUp(pixels));
136}
137
138void MailWebEngineView::scrollDown(int pixels)
139{
140 runJavaScriptInWordId(WebEngineViewer::WebEngineScript::scrollDown(pixels));
141}
142
143void MailWebEngineView::selectAll()
144{
145 page()->triggerAction(QWebEnginePage::SelectAll);
146}
147
148void MailWebEngineView::slotZoomChanged(qreal zoom)
149{
150 setZoomFactor(zoom);
151}
152
153void MailWebEngineView::scamCheck()
154{
155 d->mScamDetection->scanPage(page());
156}
157
158void MailWebEngineView::slotShowDetails()
159{
160 d->mScamDetection->showDetails();
161}
162
163void MailWebEngineView::forwardKeyReleaseEvent(QKeyEvent *e)
164{
165 if (MessageViewer::MessageViewerSettings::self()->accessKeyEnabled()) {
166 d->mWebViewAccessKey->keyReleaseEvent(e);
167 }
168}
169
170void MailWebEngineView::forwardMousePressEvent(QMouseEvent *event)
171{
172 if (d->mViewer && !d->mHoveredUrl.isEmpty()) {
173 if (event->button() == Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier)) {
174 // special processing for shift+click
175 if (URLHandlerManager::instance()->handleShiftClick(d->mHoveredUrl, d->mViewer)) {
176 event->accept();
177 return;
178 }
179 }
180 if (event->button() == Qt::LeftButton) {
181 d->mCanStartDrag = URLHandlerManager::instance()->willHandleDrag(d->mHoveredUrl, d->mViewer);
182 d->mLastClickPosition = event->pos();
183 }
184 }
185}
186
187void MailWebEngineView::forwardMouseMoveEvent(QMouseEvent *event)
188{
189 if (d->mViewer && !d->mHoveredUrl.isEmpty()) {
190 // If we are potentially handling a drag, deal with that.
191 if (d->mCanStartDrag && (event->buttons() & Qt::LeftButton)) {
192 if ((d->mLastClickPosition - event->pos()).manhattanLength() > QApplication::startDragDistance()) {
193 if (URLHandlerManager::instance()->handleDrag(d->mHoveredUrl, d->mViewer)) {
194 // If the URL handler manager started a drag, don't handle this in the future
195 d->mCanStartDrag = false;
196 }
197 }
198 event->accept();
199 }
200 }
201}
202
203void MailWebEngineView::forwardMouseReleaseEvent(QMouseEvent *event)
204{
205 Q_UNUSED(event)
206 d->mCanStartDrag = false;
207}
208
209void MailWebEngineView::forwardKeyPressEvent(QKeyEvent *e)
210{
211 if (e && hasFocus()) {
212 if (MessageViewer::MessageViewerSettings::self()->accessKeyEnabled()) {
213 d->mWebViewAccessKey->keyPressEvent(e);
214 }
215 }
216}
217
218void MailWebEngineView::forwardWheelEvent(QWheelEvent *e)
219{
220 if (MessageViewer::MessageViewerSettings::self()->accessKeyEnabled()) {
221 d->mWebViewAccessKey->wheelEvent(e);
222 }
224 const int numDegrees = e->angleDelta().y() / 8;
225 const int numSteps = numDegrees / 15;
226 Q_EMIT wheelZoomChanged(numSteps);
227 e->accept();
228 }
229}
230
231void MailWebEngineView::resizeEvent(QResizeEvent *e)
232{
233 if (MessageViewer::MessageViewerSettings::self()->accessKeyEnabled()) {
234 d->mWebViewAccessKey->resizeEvent(e);
235 }
236 QWebEngineView::resizeEvent(e);
237}
238
239void MailWebEngineView::saveMainFrameScreenshotInFile(const QString &filename)
240{
241 // TODO need to verify it
243 image.fill(Qt::transparent);
244
245 QPainter painter(&image);
246 painter.setRenderHint(QPainter::Antialiasing, true);
247 painter.setRenderHint(QPainter::TextAntialiasing, true);
248 painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
249 render(&painter);
250 painter.end();
251 image.save(filename);
252}
253
254void MailWebEngineView::showAccessKeys()
255{
256 d->mWebViewAccessKey->showAccessKeys();
257}
258
259void MailWebEngineView::hideAccessKeys()
260{
261 d->mWebViewAccessKey->hideAccessKeys();
262}
263
264void MailWebEngineView::isScrolledToBottom()
265{
266 page()->runJavaScript(WebEngineViewer::WebEngineScript::isScrolledToBottom(),
267 WebEngineViewer::WebEngineManageScript::scriptWordId(),
268 invoke(this, &MailWebEngineView::handleIsScrolledToBottom));
269}
270
271void MailWebEngineView::setElementByIdVisible(const QString &id, bool visible)
272{
273 runJavaScriptInWordId(WebEngineViewer::WebEngineScript::setElementByIdVisible(id, visible));
274}
275
276void MailWebEngineView::removeAttachmentMarking(const QString &id)
277{
278 runJavaScriptInWordId(WebEngineViewer::WebEngineScript::removeStyleToElement(id));
279}
280
281void MailWebEngineView::markAttachment(const QString &id, const QString &style)
282{
283 runJavaScriptInWordId(WebEngineViewer::WebEngineScript::setStyleToElement(id, style));
284}
285
286void MailWebEngineView::scrollToAnchor(const QString &anchor)
287{
288 page()->runJavaScript(WebEngineViewer::WebEngineScript::searchElementPosition(anchor),
289 WebEngineViewer::WebEngineManageScript::scriptWordId(),
290 invoke(this, &MailWebEngineView::handleScrollToAnchor));
291}
292
293void MailWebEngineView::handleIsScrolledToBottom(const QVariant &result)
294{
295 bool scrolledToBottomResult = false;
296 if (result.isValid()) {
297 scrolledToBottomResult = result.toBool();
298 }
299 Q_EMIT pageIsScrolledToBottom(scrolledToBottomResult);
300}
301
302void MailWebEngineView::handleScrollToAnchor(const QVariant &result)
303{
304 if (result.isValid()) {
305 const QList<QVariant> lst = result.toList();
306 if (lst.count() == 2) {
307 const QPoint pos(lst.at(0).toInt(), lst.at(1).toInt());
308 runJavaScriptInWordId(WebEngineViewer::WebEngineScript::scrollToPosition(pos));
309 }
310 }
311}
312
313void MailWebEngineView::scrollPageDown(int percent)
314{
315 runJavaScriptInWordId(WebEngineViewer::WebEngineScript::scrollPercentage(percent));
316}
317
318void MailWebEngineView::scrollPageUp(int percent)
319{
320 scrollPageDown(-percent);
321}
322
323void MailWebEngineView::scrollToRelativePosition(qreal pos)
324{
325 runJavaScriptInWordId(WebEngineViewer::WebEngineScript::scrollToRelativePosition(pos));
326}
327
328void MailWebEngineView::setAllowExternalContent(bool b)
329{
330 if (d->mExternalReference->allowExternalContent() != b) {
331 d->mExternalReference->setAllowExternalContent(b);
332 reload();
333 }
334}
335
336QList<QAction *> MailWebEngineView::interceptorUrlActions(const WebEngineViewer::WebHitTestResult &result) const
337{
338 return d->mNetworkAccessManager->interceptorUrlActions(result);
339}
340
341void MailWebEngineView::slotLoadFinished()
342{
343 scamCheck();
344}
345
346void MailWebEngineView::setPrintElementBackground(bool printElementBackground)
347{
348 d->mPageEngine->setPrintElementBackground(printElementBackground);
349}
350
351void MailWebEngineView::initializeCustomScheme()
352{
353 QWebEngineUrlScheme cidScheme("cid");
354 cidScheme.setFlags(QWebEngineUrlScheme::SecureScheme | QWebEngineUrlScheme::ContentSecurityPolicyIgnored | QWebEngineUrlScheme::LocalScheme
355 | QWebEngineUrlScheme::LocalAccessAllowed);
356 cidScheme.setSyntax(QWebEngineUrlScheme::Syntax::Path);
357 QWebEngineUrlScheme::registerScheme(cidScheme);
358}
359
360#include "moc_mailwebengineview.cpp"
The MailWebEnginePage class.
void popupMenu(const WebEngineViewer::WebHitTestResult &result)
Emitted when the user right-clicks somewhere.
The ScamDetectionWebEngine class.
The BlockMailTrackingUrlInterceptor class.
The InterceptorManager class.
The WebEngineAccessKey class.
The WebHitTestResult class.
The WebHitTest class.
Definition webhittest.h:23
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
const QList< QKeySequence > & reload()
const QPoint & pos() const const
void accept()
Qt::KeyboardModifiers keyboardModifiers()
Format_ARGB32_Premultiplied
const_reference at(qsizetype i) const const
qsizetype count() const const
int y() const const
WheelFocus
transparent
ShiftModifier
LeftButton
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isValid() const const
bool toBool() const const
QList< QVariant > toList() const const
QPoint angleDelta() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:54:19 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.