1 /*
2 SPDX-FileCopyrightText: 2015-2021 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "articleviewerwebengine.h"
8 #include "actionmanager.h"
9 #include "actions/actions.h"
10 #include "akregator_debug.h"
11 #include "akregatorconfig.h"
12 #include "articleviewerwebenginepage.h"
13 #include "urlhandler/webengine/urlhandlerwebengine.h"
14 #include "webengine/urlhandlerwebenginemanager.h"
15 #include <KPIMTextEdit/TextToSpeech>
16 #include <WebEngineViewer/InterceptorManager>
17 #include <WebEngineViewer/WebEngineAccessKey>
18 #include <WebEngineViewer/WebEngineManageScript>
19 #include <WebEngineViewer/ZoomActionMenu>
20
21 #include <GrantleeTheme/GrantleeThemeManager>
22 #include <KAboutData>
23 #include <KActionCollection>
24 #include <KGuiItem>
25 #include <KIO/FileCopyJob>
26 #include <KJobUiDelegate>
27 #include <KJobWidgets>
28 #include <KLocalizedString>
29 #include <KMessageBox>
30 #include <QApplication>
31 #include <QClipboard>
32 #include <QFileDialog>
33 #include <QMenu>
34 #include <QMouseEvent>
35 #include <QPrinter>
36 #include <QWebEngineProfile>
37 #include <QWebEngineUrlRequestInterceptor>
38 #include <viewerplugintoolmanager.h>
39
40 #include <WebEngineViewer/BlockExternalResourcesUrlInterceptor>
41 #include <WebEngineViewer/BlockTrackingUrlInterceptor>
42 #include <WebEngineViewer/LoadExternalReferencesUrlInterceptor>
43 #include <WebEngineViewer/WebEngineScript>
44 #include <WebEngineViewer/WebHitTest>
45 #include <WebEngineViewer/WebHitTestResult>
46
47 #include <WebEngineViewer/LocalDataBaseManager>
48
49 #include <KIO/KUriFilterSearchProviderActions>
50
51 using namespace Akregator;
52
53 class AkregatorRequestInterceptor : public QWebEngineUrlRequestInterceptor
54 {
55 Q_OBJECT
56
57 public:
AkregatorRequestInterceptor(QObject * parent=nullptr)58 explicit AkregatorRequestInterceptor(QObject *parent = nullptr)
59 : QWebEngineUrlRequestInterceptor(parent)
60 {
61 }
62
interceptRequest(QWebEngineUrlRequestInfo & info)63 void interceptRequest(QWebEngineUrlRequestInfo &info) override
64 {
65 Q_UNUSED(info)
66 }
67 };
68 #define HAVE_BLOCK_SUPPORT 1
ArticleViewerWebEngine(KActionCollection * ac,QWidget * parent)69 ArticleViewerWebEngine::ArticleViewerWebEngine(KActionCollection *ac, QWidget *parent)
70 : WebEngineViewer::WebEngineView(parent)
71 , mActionCollection(ac)
72 {
73 #ifndef HAVE_BLOCK_SUPPORT
74 mNetworkAccessManager = new WebEngineViewer::InterceptorManager(this, ac, this);
75 #endif
76 mPageEngine = new ArticleViewerWebEnginePage(this);
77 QWebEngineProfile *profile = mPageEngine->profile();
78 profile->setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies);
79 #ifndef HAVE_BLOCK_SUPPORT
80 // Needed to workaround crash in webengine, see https://bugreports.qt.io/browse/QTBUG-72260
81 auto webEngineUrlInterceptor = new AkregatorRequestInterceptor();
82 connect(profile, &QObject::destroyed, webEngineUrlInterceptor, &AkregatorRequestInterceptor::deleteLater);
83 profile->setUrlRequestInterceptor(webEngineUrlInterceptor);
84 #endif
85 setPage(mPageEngine);
86
87 #ifdef HAVE_BLOCK_SUPPORT
88 mNetworkAccessManager = new WebEngineViewer::InterceptorManager(this, ac, this);
89 mExternalReference = new WebEngineViewer::LoadExternalReferencesUrlInterceptor(this);
90 mExternalReference->setAllowExternalContent(Settings::self()->loadExternalReferences());
91 // connect(externalReference, &MessageViewer::LoadExternalReferencesUrlInterceptor::urlBlocked, this, &MailWebEngineView::urlBlocked);
92 mNetworkAccessManager->addInterceptor(mExternalReference);
93 auto blockTracking = new WebEngineViewer::BlockTrackingUrlInterceptor(this);
94 connect(blockTracking, &WebEngineViewer::BlockTrackingUrlInterceptor::trackingFound, this, &ArticleViewerWebEngine::trackingFound);
95 mNetworkAccessManager->addInterceptor(blockTracking);
96 mBlockExternalReference = new WebEngineViewer::BlockExternalResourcesUrlInterceptor(this);
97 // TODO connect(mBlockExternalReference, &WebEngineViewer::BlockExternalResourcesUrlInterceptor::formSubmittedForbidden, this,
98 // &MailWebEngineView::urlBlocked);
99 #endif
100 connect(this, &ArticleViewerWebEngine::showContextMenu, this, &ArticleViewerWebEngine::slotShowContextMenu);
101
102 setFocusPolicy(Qt::WheelFocus);
103 connect(mPageEngine, &ArticleViewerWebEnginePage::urlClicked, this, &ArticleViewerWebEngine::slotLinkClicked);
104
105 mWebEngineViewAccessKey = new WebEngineViewer::WebEngineAccessKey(this, this);
106 mWebEngineViewAccessKey->setActionCollection(mActionCollection);
107 connect(mWebEngineViewAccessKey, &WebEngineViewer::WebEngineAccessKey::openUrl, this, &ArticleViewerWebEngine::slotLinkClicked);
108
109 connect(this, &ArticleViewerWebEngine::loadStarted, this, &ArticleViewerWebEngine::slotLoadStarted);
110 connect(this, &ArticleViewerWebEngine::loadFinished, this, &ArticleViewerWebEngine::slotLoadFinished);
111 connect(page(), &QWebEnginePage::linkHovered, this, &ArticleViewerWebEngine::slotLinkHovered);
112
113 setContextMenuPolicy(Qt::DefaultContextMenu);
114 mWebShortcutMenuManager = new KIO::KUriFilterSearchProviderActions(this);
115 mShareServiceManager = new PimCommon::ShareServiceUrlManager(this);
116 connect(mShareServiceManager, &PimCommon::ShareServiceUrlManager::serviceUrlSelected, this, &ArticleViewerWebEngine::slotServiceUrlSelected);
117 connect(page(), &QWebEnginePage::audioMutedChanged, this, &ArticleViewerWebEngine::slotWebPageMutedOrAudibleChanged);
118 connect(page(), &QWebEnginePage::recentlyAudibleChanged, this, &ArticleViewerWebEngine::slotWebPageMutedOrAudibleChanged);
119
120 connect(phishingDatabase(), &WebEngineViewer::LocalDataBaseManager::checkUrlFinished, this, &ArticleViewerWebEngine::slotCheckedUrlFinished);
121 }
122
~ArticleViewerWebEngine()123 ArticleViewerWebEngine::~ArticleViewerWebEngine()
124 {
125 }
126
execPrintPreviewPage(QPrinter * printer,int timeout)127 void ArticleViewerWebEngine::execPrintPreviewPage(QPrinter *printer, int timeout)
128 {
129 if (!mPageEngine->execPrintPreviewPage(printer, timeout)) {
130 qCWarning(AKREGATOR_LOG) << "Impossible to print page";
131 }
132 }
133
updateSecurity()134 void ArticleViewerWebEngine::updateSecurity()
135 {
136 mExternalReference->setAllowExternalContent(Settings::self()->loadExternalReferences());
137 }
138
slotWebPageMutedOrAudibleChanged()139 void ArticleViewerWebEngine::slotWebPageMutedOrAudibleChanged()
140 {
141 Q_EMIT webPageMutedOrAudibleChanged(page()->isAudioMuted(), page()->recentlyAudible());
142 }
143
introductionData() const144 QVariantHash ArticleViewerWebEngine::introductionData() const
145 {
146 QVariantHash data;
147 data[QStringLiteral("icon")] = QStringLiteral("akregator");
148 data[QStringLiteral("name")] = i18n("Akregator");
149 data[QStringLiteral("subtitle")] = i18n("Akregator is a KDE news feed reader.");
150 data[QStringLiteral("version")] = KAboutData::applicationData().version();
151 return data;
152 }
153
showAboutPage()154 void ArticleViewerWebEngine::showAboutPage()
155 {
156 paintAboutScreen(QStringLiteral(":/about/introduction_akregator.html"), introductionData());
157 }
158
paintAboutScreen(const QString & templateName,const QVariantHash & data)159 void ArticleViewerWebEngine::paintAboutScreen(const QString &templateName, const QVariantHash &data)
160 {
161 GrantleeTheme::ThemeManager manager(QStringLiteral("splashPage"), QStringLiteral("splash.theme"), nullptr, QStringLiteral("messageviewer/about/"));
162 GrantleeTheme::Theme theme = manager.theme(QStringLiteral("default"));
163 if (theme.isValid()) {
164 setHtml(theme.render(templateName, data, QByteArrayLiteral("akregator")), QUrl::fromLocalFile(theme.absolutePath() + QLatin1Char('/')));
165 } else {
166 qCDebug(AKREGATOR_LOG) << "Theme error: failed to find splash theme";
167 }
168 }
169
slotServiceUrlSelected(PimCommon::ShareServiceUrlManager::ServiceType type)170 void ArticleViewerWebEngine::slotServiceUrlSelected(PimCommon::ShareServiceUrlManager::ServiceType type)
171 {
172 if (mCurrentUrl.isEmpty()) {
173 return;
174 }
175 const QUrl url = mShareServiceManager->generateServiceUrl(mCurrentUrl.url(), QString(), type);
176 mShareServiceManager->openUrl(url);
177 }
178
slotSaveLinkAs()179 void ArticleViewerWebEngine::slotSaveLinkAs()
180 {
181 QUrl url(mCurrentUrl);
182 if (url.fileName().isEmpty()) {
183 url = url.adjusted(QUrl::StripTrailingSlash);
184 url.setPath(url.path() + QLatin1String("/index.html"));
185 }
186
187 auto dlg = new QFileDialog(this);
188 dlg->setAttribute(Qt::WA_DeleteOnClose);
189 dlg->setAcceptMode(QFileDialog::AcceptSave);
190 dlg->setWindowTitle(i18nc("@title:window", "Save As"));
191 dlg->setOption(QFileDialog::DontConfirmOverwrite, false);
192 dlg->selectFile(url.fileName());
193 dlg->show();
194 connect(dlg, &QFileDialog::urlSelected, this, [this, url](const QUrl &destURL) {
195 if (destURL.isValid()) {
196 KIO::FileCopyJob *job = KIO::file_copy(url, destURL, -1, KIO::Overwrite);
197 job->addMetaData(QStringLiteral("MaxCacheSize"), QStringLiteral("0")); // Don't store in http cache.
198 job->addMetaData(QStringLiteral("cache"), QStringLiteral("cache")); // Use entry from cache if available.
199 KJobWidgets::setWindow(job, this);
200 job->uiDelegate()->setAutoErrorHandlingEnabled(true);
201 }
202 });
203 }
204
slotSaveImageOnDiskInFrame()205 void ArticleViewerWebEngine::slotSaveImageOnDiskInFrame()
206 {
207 slotSaveLinkAs();
208 }
209
slotCopyImageLocationInFrame()210 void ArticleViewerWebEngine::slotCopyImageLocationInFrame()
211 {
212 slotCopyLinkAddress();
213 }
214
slotMute(bool mute)215 void ArticleViewerWebEngine::slotMute(bool mute)
216 {
217 page()->setAudioMuted(mute);
218 }
219
slotCopyLinkAddress()220 void ArticleViewerWebEngine::slotCopyLinkAddress()
221 {
222 if (mCurrentUrl.isEmpty()) {
223 return;
224 }
225 QClipboard *cb = QApplication::clipboard();
226 cb->setText(mCurrentUrl.toString(), QClipboard::Clipboard);
227 // don't set url to selection as it's a no-no according to a fd.o spec
228 // which spec? Nobody seems to care (tested Firefox (3.5.10) Konqueror,and KMail (4.2.3)), so I re-enable the following line unless someone gives
229 // a good reason to remove it again (bug 183022) --Frank
230 cb->setText(mCurrentUrl.toString(), QClipboard::Selection);
231 }
232
contextMenuEvent(QContextMenuEvent * e)233 void ArticleViewerWebEngine::contextMenuEvent(QContextMenuEvent *e)
234 {
235 displayContextMenu(e->pos());
236 }
237
slotShowContextMenu(const QPoint & pos)238 void ArticleViewerWebEngine::slotShowContextMenu(const QPoint &pos)
239 {
240 displayContextMenu(pos);
241 }
242
slotCopy()243 void ArticleViewerWebEngine::slotCopy()
244 {
245 triggerPageAction(QWebEnginePage::Copy);
246 }
247
slotLoadFinished()248 void ArticleViewerWebEngine::slotLoadFinished()
249 {
250 restoreCurrentPosition();
251 unsetCursor();
252 }
253
slotLoadStarted()254 void ArticleViewerWebEngine::slotLoadStarted()
255 {
256 mWebEngineViewAccessKey->hideAccessKeys();
257 setCursor(Qt::WaitCursor);
258 }
259
slotWebHitFinished(const WebEngineViewer::WebHitTestResult & result)260 void ArticleViewerWebEngine::slotWebHitFinished(const WebEngineViewer::WebHitTestResult &result)
261 {
262 mCurrentUrl = result.linkUrl();
263 if (URLHandlerWebEngineManager::instance()->handleContextMenuRequest(mCurrentUrl, mapToGlobal(result.pos()), this)) {
264 return;
265 }
266
267 QMenu popup(this);
268 const bool noContentSelected = selectedText().isEmpty();
269 if (noContentSelected) {
270 if (!mCurrentUrl.isEmpty()) {
271 {
272 QAction *act = createOpenLinkInNewTabAction(mCurrentUrl, &popup);
273 connect(act, &QAction::triggered, this, &ArticleViewerWebEngine::slotOpenLinkInBackgroundTab);
274 popup.addAction(act);
275 }
276 {
277 QAction *act = createOpenLinkInExternalBrowserAction(mCurrentUrl, &popup);
278 connect(act, &QAction::triggered, this, &ArticleViewerWebEngine::slotOpenLinkInBrowser);
279 popup.addAction(act);
280 }
281 popup.addSeparator();
282 popup.addAction(mActionCollection->action(QStringLiteral("savelinkas")));
283 popup.addAction(mActionCollection->action(QStringLiteral("copylinkaddress")));
284 }
285 if (!result.imageUrl().isEmpty()) {
286 popup.addSeparator();
287 popup.addAction(mActionCollection->action(QStringLiteral("copy_image_location")));
288 popup.addAction(mActionCollection->action(QStringLiteral("saveas_imageurl")));
289 }
290 popup.addSeparator();
291 popup.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl));
292 popup.addSeparator();
293 popup.addAction(mShareServiceManager->menu());
294 } else {
295 popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_copy")));
296 popup.addSeparator();
297 mWebShortcutMenuManager->setSelectedText(page()->selectedText());
298 mWebShortcutMenuManager->addWebShortcutsToMenu(&popup);
299 popup.addSeparator();
300 popup.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection));
301 }
302 popup.addSeparator();
303 popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_print")));
304 popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_printpreview")));
305 popup.addSeparator();
306 popup.addAction(ActionManager::getInstance()->action(QStringLiteral("tab_mute")));
307 popup.addAction(ActionManager::getInstance()->action(QStringLiteral("tab_unmute")));
308 popup.addSeparator();
309 popup.addAction(ActionManager::getInstance()->action(QStringLiteral("find_in_messages")));
310 if (KPIMTextEdit::TextToSpeech::self()->isReady()) {
311 popup.addSeparator();
312 popup.addAction(ActionManager::getInstance()->action(QStringLiteral("speak_text")));
313 }
314 popup.exec(mapToGlobal(result.pos()));
315 }
316
displayContextMenu(const QPoint & pos)317 void ArticleViewerWebEngine::displayContextMenu(const QPoint &pos)
318 {
319 WebEngineViewer::WebHitTest *webHit = mPageEngine->hitTestContent(pos);
320 connect(webHit, &WebEngineViewer::WebHitTest::finished, this, &ArticleViewerWebEngine::slotWebHitFinished);
321 }
322
slotLinkHovered(const QString & link)323 void ArticleViewerWebEngine::slotLinkHovered(const QString &link)
324 {
325 QString msg = URLHandlerWebEngineManager::instance()->statusBarMessage(QUrl(link), this);
326 if (msg.isEmpty()) {
327 msg = link;
328 }
329
330 Q_EMIT showStatusBarMessage(msg);
331 }
332
forwardKeyReleaseEvent(QKeyEvent * e)333 void ArticleViewerWebEngine::forwardKeyReleaseEvent(QKeyEvent *e)
334 {
335 if (Settings::self()->accessKeyEnabled()) {
336 mWebEngineViewAccessKey->keyReleaseEvent(e);
337 }
338 }
339
forwardKeyPressEvent(QKeyEvent * e)340 void ArticleViewerWebEngine::forwardKeyPressEvent(QKeyEvent *e)
341 {
342 if (e && hasFocus()) {
343 if (Settings::self()->accessKeyEnabled()) {
344 mWebEngineViewAccessKey->keyPressEvent(e);
345 }
346 }
347 }
348
forwardWheelEvent(QWheelEvent * e)349 void ArticleViewerWebEngine::forwardWheelEvent(QWheelEvent *e)
350 {
351 if (Settings::self()->accessKeyEnabled()) {
352 mWebEngineViewAccessKey->wheelEvent(e);
353 }
354 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
355 const int numDegrees = e->angleDelta().y() / 8;
356 const int numSteps = numDegrees / 15;
357 const qreal factor = ActionManager::getInstance()->zoomActionMenu()->zoomFactor() + numSteps * 10;
358 if (factor >= 10 && factor <= 300) {
359 ActionManager::getInstance()->zoomActionMenu()->setZoomFactor(factor);
360 ActionManager::getInstance()->zoomActionMenu()->setWebViewerZoomFactor(factor / 100.0);
361 }
362 e->accept();
363 }
364 }
365
resizeEvent(QResizeEvent * e)366 void ArticleViewerWebEngine::resizeEvent(QResizeEvent *e)
367 {
368 if (Settings::self()->accessKeyEnabled()) {
369 mWebEngineViewAccessKey->resizeEvent(e);
370 }
371 QWebEngineView::resizeEvent(e);
372 }
373
disableIntroduction()374 void ArticleViewerWebEngine::disableIntroduction()
375 {
376 KGuiItem yesButton(KStandardGuiItem::yes());
377 yesButton.setText(i18n("Disable"));
378 KGuiItem noButton(KStandardGuiItem::no());
379 noButton.setText(i18n("Keep Enabled"));
380 if (KMessageBox::questionYesNo(this,
381 i18n("Are you sure you want to disable this introduction page?"),
382 i18n("Disable Introduction Page"),
383 yesButton,
384 noButton)
385 == KMessageBox::Yes) {
386 Settings::self()->setDisableIntroduction(true);
387 Settings::self()->save();
388 }
389 }
390
setArticleAction(ArticleViewerWebEngine::ArticleAction type,const QString & articleId,const QString & feed)391 void ArticleViewerWebEngine::setArticleAction(ArticleViewerWebEngine::ArticleAction type, const QString &articleId, const QString &feed)
392 {
393 Q_EMIT articleAction(type, articleId, feed);
394 }
395
restoreCurrentPosition()396 void ArticleViewerWebEngine::restoreCurrentPosition()
397 {
398 mPageEngine->runJavaScript(WebEngineViewer::WebEngineScript::scrollToRelativePosition(0), WebEngineViewer::WebEngineManageScript::scriptWordId());
399 }
400
forwardMouseReleaseEvent(QMouseEvent * event)401 void ArticleViewerWebEngine::forwardMouseReleaseEvent(QMouseEvent *event)
402 {
403 if (event->button() & Qt::RightButton) {
404 Q_EMIT showContextMenu(event->pos());
405 mLastButtonClicked = RightButton;
406 } else if (event->button() & Qt::MiddleButton) {
407 mLastButtonClicked = MiddleButton;
408 } else if (event->button() & Qt::LeftButton) {
409 mLastButtonClicked = LeftButton;
410 }
411 }
412
urlIsAMalwareButContinue()413 bool ArticleViewerWebEngine::urlIsAMalwareButContinue()
414 {
415 if (KMessageBox::No
416 == KMessageBox::warningYesNo(this,
417 i18n("This web site is a malware, do you want to continue to show it?"),
418 i18n("Malware"),
419 KStandardGuiItem::cont(),
420 KStandardGuiItem::cancel())) {
421 return false;
422 }
423 return true;
424 }
425
slotCheckedUrlFinished(const QUrl & url,WebEngineViewer::CheckPhishingUrlUtil::UrlStatus status)426 void ArticleViewerWebEngine::slotCheckedUrlFinished(const QUrl &url, WebEngineViewer::CheckPhishingUrlUtil::UrlStatus status)
427 {
428 switch (status) {
429 case WebEngineViewer::CheckPhishingUrlUtil::BrokenNetwork:
430 KMessageBox::error(this, i18n("The network is broken."), i18n("Check Phishing Url"));
431 break;
432 case WebEngineViewer::CheckPhishingUrlUtil::InvalidUrl:
433 KMessageBox::error(this, i18n("The url %1 is not valid.", url.toString()), i18n("Check Phishing Url"));
434 break;
435 case WebEngineViewer::CheckPhishingUrlUtil::Ok:
436 break;
437 case WebEngineViewer::CheckPhishingUrlUtil::MalWare:
438 if (!urlIsAMalwareButContinue()) {
439 return;
440 }
441 break;
442 case WebEngineViewer::CheckPhishingUrlUtil::Unknown:
443 qCWarning(AKREGATOR_LOG) << "ArticleViewerWebEngine::slotCheckedUrlFinished unknown error ";
444 break;
445 }
446 openSafeUrl(url);
447 }
448
slotLinkClicked(const QUrl & url)449 void ArticleViewerWebEngine::slotLinkClicked(const QUrl &url)
450 {
451 if (URLHandlerWebEngineManager::instance()->handleClick(url, this)) {
452 return;
453 }
454 if (Settings::checkPhishingUrl()) {
455 phishingDatabase()->checkUrl(url);
456 } else {
457 openSafeUrl(url);
458 }
459 }
460
openSafeUrl(const QUrl & url)461 void ArticleViewerWebEngine::openSafeUrl(const QUrl &url)
462 {
463 mCurrentUrl = url;
464 OpenUrlRequest req(mCurrentUrl);
465 if (mLastButtonClicked == LeftButton) {
466 switch (Settings::lMBBehaviour()) {
467 case Settings::EnumLMBBehaviour::OpenInExternalBrowser:
468 req.setOptions(OpenUrlRequest::ExternalBrowser);
469 break;
470 case Settings::EnumLMBBehaviour::OpenInBackground:
471 req.setOpenInBackground(true);
472 req.setOptions(OpenUrlRequest::NewTab);
473 break;
474 default:
475 break;
476 }
477 } else if (mLastButtonClicked == MiddleButton) {
478 switch (Settings::mMBBehaviour()) {
479 case Settings::EnumMMBBehaviour::OpenInExternalBrowser:
480 req.setOptions(OpenUrlRequest::ExternalBrowser);
481 break;
482 case Settings::EnumMMBBehaviour::OpenInBackground:
483 req.setOpenInBackground(true);
484 req.setOptions(OpenUrlRequest::NewTab);
485 break;
486 default:
487 break;
488 }
489 }
490 Q_EMIT signalOpenUrlRequest(req);
491 }
492
slotOpenLinkInForegroundTab()493 void ArticleViewerWebEngine::slotOpenLinkInForegroundTab()
494 {
495 OpenUrlRequest req(mCurrentUrl);
496 req.setOptions(OpenUrlRequest::NewTab);
497 Q_EMIT signalOpenUrlRequest(req);
498 }
499
slotOpenLinkInBackgroundTab()500 void ArticleViewerWebEngine::slotOpenLinkInBackgroundTab()
501 {
502 OpenUrlRequest req(mCurrentUrl);
503 req.setOptions(OpenUrlRequest::NewTab);
504 req.setOpenInBackground(true);
505 Q_EMIT signalOpenUrlRequest(req);
506 }
507
slotOpenLinkInBrowser()508 void ArticleViewerWebEngine::slotOpenLinkInBrowser()
509 {
510 OpenUrlRequest req(mCurrentUrl);
511 req.setOptions(OpenUrlRequest::ExternalBrowser);
512 Q_EMIT signalOpenUrlRequest(req);
513 }
514
createViewerPluginToolManager(KActionCollection * ac,QWidget * parent)515 void ArticleViewerWebEngine::createViewerPluginToolManager(KActionCollection *ac, QWidget *parent)
516 {
517 mViewerPluginToolManager = new MessageViewer::ViewerPluginToolManager(parent, this);
518 mViewerPluginToolManager->setActionCollection(ac);
519 mViewerPluginToolManager->setPluginName(QStringLiteral("akregator"));
520 mViewerPluginToolManager->setPluginDirectory(QStringLiteral("akregator/viewerplugin"));
521 if (!mViewerPluginToolManager->initializePluginList()) {
522 qCWarning(AKREGATOR_LOG) << " Impossible to initialize plugins";
523 }
524 mViewerPluginToolManager->createView();
525 connect(mViewerPluginToolManager, &MessageViewer::ViewerPluginToolManager::activatePlugin, this, &ArticleViewerWebEngine::slotActivatePlugin);
526 }
527
viewerPluginActionList(MessageViewer::ViewerPluginInterface::SpecificFeatureTypes features)528 QList<QAction *> ArticleViewerWebEngine::viewerPluginActionList(MessageViewer::ViewerPluginInterface::SpecificFeatureTypes features)
529 {
530 if (mViewerPluginToolManager) {
531 return mViewerPluginToolManager->viewerPluginActionList(features);
532 }
533 return QList<QAction *>();
534 }
535
slotActivatePlugin(MessageViewer::ViewerPluginInterface * interface)536 void ArticleViewerWebEngine::slotActivatePlugin(MessageViewer::ViewerPluginInterface *interface)
537 {
538 const QString text = selectedText();
539 if (!text.isEmpty()) {
540 interface->setText(text);
541 }
542 interface->setUrl(mCurrentUrl);
543 interface->execute();
544 }
545
546 #include "articleviewerwebengine.moc"
547