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

KDE's Doxygen guidelines are available online.