Messagelib

mailwebengineview.cpp
1 /*
2  SPDX-FileCopyrightText: 2016-2023 Laurent Montel <[email protected]>
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 
30 using namespace MessageViewer;
31 template<typename Arg, typename R, typename C>
32 struct InvokeWrapper {
33  R *receiver;
34  void (C::*memberFunction)(Arg);
35  void operator()(Arg result)
36  {
37  (receiver->*memberFunction)(result);
38  }
39 };
40 
41 template<typename Arg, typename R, typename C>
42 
43 InvokeWrapper<Arg, R, C> invoke(R *receiver, void (C::*memberFunction)(Arg))
44 {
45  InvokeWrapper<Arg, R, C> wrapper = {receiver, memberFunction};
46  return wrapper;
47 }
48 
49 class MessageViewer::MailWebEngineViewPrivate
50 {
51 public:
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 
66 MailWebEngineView::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 
100 MailWebEngineView::~MailWebEngineView() = default;
101 
102 void MailWebEngineView::readConfig()
103 {
104  d->mBlockMailTrackingUrl->setEnabledMailTrackingInterceptor(MessageViewer::MessageViewerSettings::self()->mailTrackingUrlEnabled());
105 }
106 
107 void MailWebEngineView::setLinkHovered(const QUrl &url)
108 {
109  // TODO we need to detect image url too.
110  d->mHoveredUrl = url;
111 }
112 
113 void MailWebEngineView::runJavaScriptInWordId(const QString &script)
114 {
115  page()->runJavaScript(script, WebEngineViewer::WebEngineManageScript::scriptWordId());
116 }
117 
118 void MailWebEngineView::setViewer(MessageViewer::ViewerPrivate *viewer)
119 {
120  d->mViewer = viewer;
121 }
122 
123 void 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 
129 void MailWebEngineView::slotWebHitFinished(const WebEngineViewer::WebHitTestResult &result)
130 {
131  Q_EMIT popupMenu(result);
132 }
133 
134 void MailWebEngineView::scrollUp(int pixels)
135 {
136  runJavaScriptInWordId(WebEngineViewer::WebEngineScript::scrollUp(pixels));
137 }
138 
139 void MailWebEngineView::scrollDown(int pixels)
140 {
141  runJavaScriptInWordId(WebEngineViewer::WebEngineScript::scrollDown(pixels));
142 }
143 
144 void MailWebEngineView::selectAll()
145 {
146  page()->triggerAction(QWebEnginePage::SelectAll);
147 }
148 
149 void MailWebEngineView::slotZoomChanged(qreal zoom)
150 {
151  setZoomFactor(zoom);
152 }
153 
154 void MailWebEngineView::scamCheck()
155 {
156  d->mScamDetection->scanPage(page());
157 }
158 
159 void MailWebEngineView::slotShowDetails()
160 {
161  d->mScamDetection->showDetails();
162 }
163 
164 void MailWebEngineView::forwardKeyReleaseEvent(QKeyEvent *e)
165 {
166  if (MessageViewer::MessageViewerSettings::self()->accessKeyEnabled()) {
167  d->mWebViewAccessKey->keyReleaseEvent(e);
168  }
169 }
170 
171 void 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 
188 void 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 
204 void MailWebEngineView::forwardMouseReleaseEvent(QMouseEvent *event)
205 {
206  Q_UNUSED(event)
207  d->mCanStartDrag = false;
208 }
209 
210 void MailWebEngineView::forwardKeyPressEvent(QKeyEvent *e)
211 {
212  if (e && hasFocus()) {
213  if (MessageViewer::MessageViewerSettings::self()->accessKeyEnabled()) {
214  d->mWebViewAccessKey->keyPressEvent(e);
215  }
216  }
217 }
218 
219 void 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 
232 void MailWebEngineView::resizeEvent(QResizeEvent *e)
233 {
234  if (MessageViewer::MessageViewerSettings::self()->accessKeyEnabled()) {
235  d->mWebViewAccessKey->resizeEvent(e);
236  }
237  QWebEngineView::resizeEvent(e);
238 }
239 
240 void 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 
255 void MailWebEngineView::showAccessKeys()
256 {
257  d->mWebViewAccessKey->showAccessKeys();
258 }
259 
260 void MailWebEngineView::hideAccessKeys()
261 {
262  d->mWebViewAccessKey->hideAccessKeys();
263 }
264 
265 void MailWebEngineView::isScrolledToBottom()
266 {
267  page()->runJavaScript(WebEngineViewer::WebEngineScript::isScrolledToBottom(),
268  WebEngineViewer::WebEngineManageScript::scriptWordId(),
269  invoke(this, &MailWebEngineView::handleIsScrolledToBottom));
270 }
271 
272 void MailWebEngineView::setElementByIdVisible(const QString &id, bool visible)
273 {
274  runJavaScriptInWordId(WebEngineViewer::WebEngineScript::setElementByIdVisible(id, visible));
275 }
276 
277 void MailWebEngineView::removeAttachmentMarking(const QString &id)
278 {
279  runJavaScriptInWordId(WebEngineViewer::WebEngineScript::removeStyleToElement(id));
280 }
281 
282 void MailWebEngineView::markAttachment(const QString &id, const QString &style)
283 {
284  runJavaScriptInWordId(WebEngineViewer::WebEngineScript::setStyleToElement(id, style));
285 }
286 
287 void MailWebEngineView::scrollToAnchor(const QString &anchor)
288 {
289  page()->runJavaScript(WebEngineViewer::WebEngineScript::searchElementPosition(anchor),
290  WebEngineViewer::WebEngineManageScript::scriptWordId(),
291  invoke(this, &MailWebEngineView::handleScrollToAnchor));
292 }
293 
294 void 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 
303 void 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 
314 void MailWebEngineView::scrollPageDown(int percent)
315 {
316  runJavaScriptInWordId(WebEngineViewer::WebEngineScript::scrollPercentage(percent));
317 }
318 
319 void MailWebEngineView::scrollPageUp(int percent)
320 {
321  scrollPageDown(-percent);
322 }
323 
324 void MailWebEngineView::scrollToRelativePosition(qreal pos)
325 {
326  runJavaScriptInWordId(WebEngineViewer::WebEngineScript::scrollToRelativePosition(pos));
327 }
328 
329 void MailWebEngineView::setAllowExternalContent(bool b)
330 {
331  if (d->mExternalReference->allowExternalContent() != b) {
332  d->mExternalReference->setAllowExternalContent(b);
333  reload();
334  }
335 }
336 
337 QList<QAction *> MailWebEngineView::interceptorUrlActions(const WebEngineViewer::WebHitTestResult &result) const
338 {
339  return d->mNetworkAccessManager->interceptorUrlActions(result);
340 }
341 
342 void MailWebEngineView::slotLoadFinished()
343 {
344  scamCheck();
345 }
346 
347 void MailWebEngineView::setPrintElementBackground(bool printElementBackground)
348 {
349  d->mPageEngine->setPrintElementBackground(printElementBackground);
350 }
351 
352 bool MailWebEngineView::execPrintPreviewPage(QPrinter *printer, int timeout)
353 {
354  return d->mPageEngine->execPrintPreviewPage(printer, timeout);
355 }
356 
357 void 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 }
Qt::KeyboardModifiers keyboardModifiers()
bool isValid() const const
Format_ARGB32_Premultiplied
The WebHitTest class.
Definition: webhittest.h:22
int count(const T &value) const const
const QList< QKeySequence > & reload()
QPoint angleDelta() const const
The ScamDetectionWebEngine class.
int y() const const
The WebHitTestResult class.
LeftButton
The InterceptorManager class.
const QPoint & pos() const const
const T & at(int i) const const
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void popupMenu(const WebEngineViewer::WebHitTestResult &result)
Emitted when the user right-clicks somewhere.
bool toBool() const const
The MailWebEnginePage class.
The BlockMailTrackingUrlInterceptor class.
QList< QVariant > toList() const const
The WebEngineAccessKey class.
WheelFocus
ShiftModifier
transparent
void accept()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Mar 27 2023 04:08:17 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.