diff --git a/webenginepart/src/CMakeLists.txt b/webenginepart/src/CMakeLists.txt
index 091845db3..4b565e56d 100644
--- a/webenginepart/src/CMakeLists.txt
+++ b/webenginepart/src/CMakeLists.txt
@@ -1,78 +1,83 @@
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Wallet)
add_definitions(-DTRANSLATION_DOMAIN=\"webenginepart\")
if(BUILD_TESTING)
add_definitions(-DBUILD_TESTING)
endif(BUILD_TESTING)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR})
set(kwebenginepartlib_LIB_SRCS
webenginepart.cpp
webenginepart_ext.cpp
webengineview.cpp
webenginepage.cpp
websslinfo.cpp
webhistoryinterface.cpp
webenginepartdownloadmanager.cpp
webenginewallet.cpp
webengineparterrorschemehandler.cpp
- webengineparthtmlembedder.cpp
webenginepartkiohandler.cpp
webenginepartcookiejar.cpp
settings/webenginesettings.cpp
settings/webengine_filter.cpp
ui/searchbar.cpp
ui/passwordbar.cpp
ui/featurepermissionbar.cpp
)
+if(Qt5WebEngineWidgets_VERSION VERSION_GREATER_EQUAL "5.12.0")
+ add_definitions(-DUSE_QWEBENGINE_URL_SCHEME)
+else()
+ list(APPEND kwebenginepartlib_LIB_SRCS, webengineparthtmlembedder.cpp)
+endif()
+
qt5_wrap_ui(kwebenginepartlib_LIB_SRCS
ui/searchbar.ui
)
ecm_qt_declare_logging_category(kwebenginepartlib_LIB_SRCS
HEADER webenginepart_debug.h
IDENTIFIER WEBENGINEPART_LOG
CATEGORY_NAME org.kde.webenginepart
)
add_library(kwebenginepartlib ${kwebenginepartlib_LIB_SRCS})
generate_export_header(kwebenginepartlib)
target_link_libraries(kwebenginepartlib
PUBLIC
Qt5::Core
Qt5::DBus
Qt5::Gui
Qt5::Widgets
Qt5::WebEngineWidgets
KF5::Parts
KF5::Wallet
PRIVATE
Qt5::PrintSupport
KF5::SonnetCore
KF5::IconThemes #for KIconLoader used by WebEnginePartErrorSchemeHandler
KF5::WindowSystem # for KUserTimestamp
)
target_include_directories(kwebenginepartlib PUBLIC
"$"
)
set_target_properties(kwebenginepartlib PROPERTIES OUTPUT_NAME kwebenginepart)
install(TARGETS kwebenginepartlib ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
add_library(webenginepart MODULE webenginepartfactory.cpp)
kcoreaddons_desktop_to_json(webenginepart webenginepart.desktop)
target_link_libraries(webenginepart kwebenginepartlib)
install(TARGETS webenginepart DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/parts)
install(FILES webenginepart.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
install(FILES webenginepart.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/webenginepart)
install(FILES error.html DESTINATION ${KDE_INSTALL_DATADIR}/webenginepart)
diff --git a/webenginepart/src/webenginepart.cpp b/webenginepart/src/webenginepart.cpp
index bfd16ff4a..80d7504f9 100644
--- a/webenginepart/src/webenginepart.cpp
+++ b/webenginepart/src/webenginepart.cpp
@@ -1,959 +1,997 @@
/*
* This file is part of the KDE project.
*
* Copyright (C) 2007 Trolltech ASA
* Copyright (C) 2008 - 2010 Urs Wolfer
* Copyright (C) 2008 Laurent Montel
* Copyright (C) 2009 Dawit Alemayehu
* Copyright (C) 2013 Allan Sandfeld Jensen
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
*/
#include "webenginepart.h"
#include "webenginepartkiohandler.h"
#include
//#include
#include
#include
#include
#include "webenginepart_ext.h"
#include "webengineview.h"
#include "webenginepage.h"
#include "websslinfo.h"
#include "webhistoryinterface.h"
#include "webenginewallet.h"
#include "webengineparterrorschemehandler.h"
#include "webenginepartcookiejar.h"
#include "ui/searchbar.h"
#include "ui/passwordbar.h"
#include "ui/featurepermissionbar.h"
#include "settings/webenginesettings.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
+#ifdef USE_QWEBENGINE_URL_SCHEME
+#include
+#endif
#include "utils.h"
+#include
+
+void WebEnginePart::initWebEngineUrlSchemes()
+{
+#ifdef USE_QWEBENGINE_URL_SCHEME
+ static bool needToInitUrlSchemes = true;
+ if (needToInitUrlSchemes) {
+ needToInitUrlSchemes = false;
+ QVector localSchemes = {"error"};
+ const QStringList protocols = KProtocolInfo::protocols();
+ for(const QString &prot : protocols){
+#if KIO_VERSION >= QT_VERSION_CHECK(5,60,0)
+ if (KProtocolInfo::defaultMimetype(prot) == "text/html") {
+ localSchemes.append(QByteArray(prot.toLatin1()));
+ }
+#else
+ QUrl fakeUrl;
+ fakeUrl.setScheme(prot);
+ fakeUrl.setPath("fake");
+ if (KProtocolManager::defaultMimetype(fakeUrl) == "text/html"){
+ localSchemes.append(QByteArray(prot.toLatin1()));
+ }
+#endif
+ }
+ for (const QByteArray &name : qAsConst(localSchemes)){
+ QWebEngineUrlScheme scheme(name);
+ scheme.setFlags(QWebEngineUrlScheme::LocalScheme|QWebEngineUrlScheme::LocalAccessAllowed);
+ scheme.setSyntax(QWebEngineUrlScheme::Syntax::Path);
+ QWebEngineUrlScheme::registerScheme(scheme);
+ }
+ }
+#endif
+}
WebEnginePart::WebEnginePart(QWidget *parentWidget, QObject *parent,
const QByteArray& cachedHistory, const QStringList& /*args*/)
:KParts::ReadOnlyPart(parent),
m_emitOpenUrlNotify(true),
m_hasCachedFormData(false),
m_doLoadFinishedActions(false),
m_statusBarWalletLabel(nullptr),
m_searchBar(nullptr),
m_passwordBar(nullptr),
m_featurePermissionBar(nullptr),
m_wallet(nullptr)
{
+ initWebEngineUrlSchemes();
QWebEngineProfile *prof = QWebEngineProfile::defaultProfile();
if (!prof->urlSchemeHandler("error")) {
prof->installUrlSchemeHandler("error", new WebEnginePartErrorSchemeHandler(prof));
prof->installUrlSchemeHandler("help", new WebEnginePartKIOHandler(prof));
}
static WebEnginePartCookieJar s_cookieJar(prof, prof);
KAboutData about = KAboutData(QStringLiteral("webenginepart"),
i18nc("Program Name", "WebEnginePart"),
/*version*/ QStringLiteral("1.3.0"),
i18nc("Short Description", "QtWebEngine Browser Engine Component"),
KAboutLicense::LGPL,
i18n("(C) 2009-2010 Dawit Alemayehu\n"
"(C) 2008-2010 Urs Wolfer\n"
"(C) 2007 Trolltech ASA"));
about.addAuthor(i18n("Sune Vuorela"), i18n("Maintainer, Developer"), QStringLiteral("sune@kde.org"));
about.addAuthor(i18n("Dawit Alemayehu"), i18n("Developer"), QStringLiteral("adawit@kde.org"));
about.addAuthor(i18n("Urs Wolfer"), i18n("Maintainer, Developer"), QStringLiteral("uwolfer@kde.org"));
about.addAuthor(i18n("Michael Howell"), i18n("Developer"), QStringLiteral("mhowell123@gmail.com"));
about.addAuthor(i18n("Laurent Montel"), i18n("Developer"), QStringLiteral("montel@kde.org"));
about.addAuthor(i18n("Dirk Mueller"), i18n("Developer"), QStringLiteral("mueller@kde.org"));
about.setProductName("webenginepart/general");
// KComponentData componentData(&about);
setComponentData(about, false /*don't load plugins yet*/);
#if 0
// NOTE: If the application does not set its version number, we automatically
// set it to KDE's version number so that the default user-agent string contains
// proper application version number information. See QWebEnginePage::userAgentForUrl...
if (QCoreApplication::applicationVersion().isEmpty())
QCoreApplication::setApplicationVersion(QString("%1.%2.%3")
.arg(KDE::versionMajor())
.arg(KDE::versionMinor())
.arg(KDE::versionRelease()));
#endif
setXMLFile(QL1S("webenginepart.rc"));
// Create this KPart's widget
QWidget *mainWidget = new QWidget (parentWidget);
mainWidget->setObjectName(QStringLiteral("webenginepart"));
// Create the WebEngineView...
m_webView = new WebEngineView (this, parentWidget);
// Create the browser extension.
m_browserExtension = new WebEngineBrowserExtension(this, cachedHistory);
// Add status bar extension...
m_statusBarExtension = new KParts::StatusBarExtension(this);
// Add a web history interface for storing visited links.
// if (!QWebEngineHistoryInterface::defaultInterface())
// QWebHistoryInterface::setDefaultInterface(new WebHistoryInterface(this));
// Add text and html extensions...
new WebEngineTextExtension(this);
new WebEngineHtmlExtension(this);
new WebEngineScriptableExtension(this);
// Layout the GUI...
QVBoxLayout* l = new QVBoxLayout(mainWidget);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
l->addWidget(m_webView);
// Set the part's widget
setWidget(mainWidget);
// Set the web view as the focus object
mainWidget->setFocusProxy(m_webView);
// Connect the signals from the webview
connect(m_webView, &QWebEngineView::titleChanged,
this, &Part::setWindowCaption);
connect(m_webView, &QWebEngineView::urlChanged,
this, &WebEnginePart::slotUrlChanged);
// connect(m_webView, SIGNAL(linkMiddleOrCtrlClicked(QUrl)),
// this, SLOT(slotLinkMiddleOrCtrlClicked(QUrl)));
// connect(m_webView, SIGNAL(selectionClipboardUrlPasted(QUrl,QString)),
// this, SLOT(slotSelectionClipboardUrlPasted(QUrl,QString)));
connect(m_webView, &QWebEngineView::loadFinished,
this, &WebEnginePart::slotLoadFinished);
// Connect the signals from the page...
connectWebEnginePageSignals(page());
// Init the QAction we are going to use...
initActions();
// Load plugins once we are fully ready
loadPlugins();
setWallet(page()->wallet());
}
WebEnginePart::~WebEnginePart()
{
}
WebEnginePage* WebEnginePart::page()
{
if (m_webView)
return qobject_cast(m_webView->page());
return nullptr;
}
const WebEnginePage* WebEnginePart::page() const
{
if (m_webView)
return qobject_cast(m_webView->page());
return nullptr;
}
void WebEnginePart::initActions()
{
actionCollection()->addAction(KStandardAction::SaveAs, QStringLiteral("saveDocument"),
m_browserExtension, SLOT(slotSaveDocument()));
QAction* action = new QAction(i18n("Save &Frame As..."), this);
actionCollection()->addAction(QStringLiteral("saveFrame"), action);
connect(action, &QAction::triggered, m_browserExtension, &WebEngineBrowserExtension::slotSaveFrame);
action = new QAction(QIcon::fromTheme(QStringLiteral("document-print-preview")), i18n("Print Preview"), this);
actionCollection()->addAction(QStringLiteral("printPreview"), action);
connect(action, &QAction::triggered, m_browserExtension, &WebEngineBrowserExtension::slotPrintPreview);
action = new QAction(QIcon::fromTheme(QStringLiteral("zoom-in")), i18nc("zoom in action", "Zoom In"), this);
actionCollection()->addAction(QStringLiteral("zoomIn"), action);
actionCollection()->setDefaultShortcuts(action, QList () << QKeySequence(QStringLiteral("CTRL++")) << QKeySequence(QStringLiteral("CTRL+=")));
connect(action, &QAction::triggered, m_browserExtension, &WebEngineBrowserExtension::zoomIn);
action = new QAction(QIcon::fromTheme(QStringLiteral("zoom-out")), i18nc("zoom out action", "Zoom Out"), this);
actionCollection()->addAction(QStringLiteral("zoomOut"), action);
actionCollection()->setDefaultShortcuts(action, QList () << QKeySequence(QStringLiteral("CTRL+-")) << QKeySequence(QStringLiteral("CTRL+_")));
connect(action, &QAction::triggered, m_browserExtension, &WebEngineBrowserExtension::zoomOut);
action = new QAction(QIcon::fromTheme(QStringLiteral("zoom-original")), i18nc("reset zoom action", "Actual Size"), this);
actionCollection()->addAction(QStringLiteral("zoomNormal"), action);
actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("CTRL+0")));
connect(action, &QAction::triggered, m_browserExtension, &WebEngineBrowserExtension::zoomNormal);
action = new QAction(i18n("Zoom Text Only"), this);
action->setCheckable(true);
KConfigGroup cgHtml(KSharedConfig::openConfig(), "HTML Settings");
bool zoomTextOnly = cgHtml.readEntry("ZoomTextOnly", false);
action->setChecked(zoomTextOnly);
actionCollection()->addAction(QStringLiteral("zoomTextOnly"), action);
connect(action, &QAction::triggered, m_browserExtension, &WebEngineBrowserExtension::toogleZoomTextOnly);
action = new QAction(i18n("Zoom To DPI"), this);
action->setCheckable(true);
bool zoomToDPI = cgHtml.readEntry("ZoomToDPI", false);
action->setChecked(zoomToDPI);
actionCollection()->addAction(QStringLiteral("zoomToDPI"), action);
connect(action, &QAction::triggered, m_browserExtension, &WebEngineBrowserExtension::toogleZoomToDPI);
action = actionCollection()->addAction(KStandardAction::SelectAll, QStringLiteral("selectAll"),
m_browserExtension, SLOT(slotSelectAll()));
action->setShortcutContext(Qt::WidgetShortcut);
m_webView->addAction(action);
KCodecAction *codecAction = new KCodecAction( QIcon::fromTheme(QStringLiteral("character-set")), i18n( "Set &Encoding" ), this, true );
actionCollection()->addAction( QStringLiteral("setEncoding"), codecAction );
connect(codecAction, SIGNAL(triggered(QTextCodec*)), SLOT(slotSetTextEncoding(QTextCodec*)));
action = new QAction(i18n("View Do&cument Source"), this);
actionCollection()->addAction(QStringLiteral("viewDocumentSource"), action);
actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_U));
connect(action, &QAction::triggered, m_browserExtension, &WebEngineBrowserExtension::slotViewDocumentSource);
action = new QAction(i18nc("Secure Sockets Layer", "SSL"), this);
actionCollection()->addAction(QStringLiteral("security"), action);
connect(action, &QAction::triggered, this, &WebEnginePart::slotShowSecurity);
action = actionCollection()->addAction(KStandardAction::Find, QStringLiteral("find"), this, SLOT(slotShowSearchBar()));
action->setWhatsThis(i18nc("find action \"whats this\" text", "Find text
"
"Shows a dialog that allows you to find text on the displayed page."));
}
void WebEnginePart::updateActions()
{
m_browserExtension->updateActions();
QAction* action = actionCollection()->action(QL1S("saveDocument"));
if (action) {
const QString protocol (url().scheme());
action->setEnabled(protocol != QL1S("about") && protocol != QL1S("error"));
}
action = actionCollection()->action(QL1S("printPreview"));
if (action) {
action->setEnabled(m_browserExtension->isActionEnabled("print"));
}
}
void WebEnginePart::connectWebEnginePageSignals(WebEnginePage* page)
{
if (!page)
return;
connect(page, SIGNAL(loadStarted()),
this, SLOT(slotLoadStarted()));
connect(page, SIGNAL(loadAborted(QUrl)),
this, SLOT(slotLoadAborted(QUrl)));
connect(page, &QWebEnginePage::linkHovered,
this, &WebEnginePart::slotLinkHovered);
// connect(page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*)),
// this, SLOT(slotSaveFrameState(QWebFrame*,QWebHistoryItem*)));
// connect(page, SIGNAL(restoreFrameStateRequested(QWebFrame*)),
// this, SLOT(slotRestoreFrameState(QWebFrame*)));
// connect(page, SIGNAL(statusBarMessage(QString)),
// this, SLOT(slotSetStatusBarText(QString)));
connect(page, SIGNAL(windowCloseRequested()),
this, SLOT(slotWindowCloseRequested()));
// connect(page, SIGNAL(printRequested(QWebFrame*)),
// m_browserExtension, SLOT(slotPrintRequested(QWebFrame*)));
// connect(page, SIGNAL(frameCreated(QWebFrame*)),
// this, SLOT(slotFrameCreated(QWebFrame*)));
// connect(m_webView, SIGNAL(linkShiftClicked(QUrl)),
// page, SLOT(downloadUrl(QUrl)));
connect(page, SIGNAL(loadProgress(int)),
m_browserExtension, SIGNAL(loadingProgress(int)));
connect(page, SIGNAL(selectionChanged()),
m_browserExtension, SLOT(updateEditActions()));
// connect(m_browserExtension, SIGNAL(saveUrl(QUrl)),
// page, SLOT(downloadUrl(QUrl)));
connect(page, &QWebEnginePage::iconUrlChanged, [page, this](const QUrl& url) {
if (WebEngineSettings::self()->favIconsEnabled()
&& !page->profile()->isOffTheRecord()){
emit m_browserExtension->setIconUrl(url);
}
});
}
void WebEnginePart::setWallet(WebEngineWallet* wallet)
{
if(m_wallet){
disconnect(m_wallet, &WebEngineWallet::saveFormDataRequested,
this, &WebEnginePart::slotSaveFormDataRequested);
disconnect(m_wallet, &WebEngineWallet::fillFormRequestCompleted,
this, &WebEnginePart::slotFillFormRequestCompleted);
disconnect(m_wallet, &WebEngineWallet::walletClosed, this, &WebEnginePart::slotWalletClosed);
}
m_wallet = wallet;
if (m_wallet) {
connect(m_wallet, &WebEngineWallet::saveFormDataRequested,
this, &WebEnginePart::slotSaveFormDataRequested);
connect(m_wallet, &WebEngineWallet::fillFormRequestCompleted,
this, &WebEnginePart::slotFillFormRequestCompleted);
connect(m_wallet, &WebEngineWallet::walletClosed, this, &WebEnginePart::slotWalletClosed);
}
}
void WebEnginePart::attemptInstallKIOSchemeHandler(const QUrl& url)
{
if (KProtocolManager::defaultMimetype(url) == "text/html") { // man:, info:, etc.
QWebEngineProfile *prof = QWebEngineProfile::defaultProfile();
QByteArray scheme = url.scheme().toUtf8();
if (!prof->urlSchemeHandler(scheme)) {
prof->installUrlSchemeHandler(scheme, new WebEnginePartKIOHandler(prof));
}
}
}
bool WebEnginePart::openUrl(const QUrl &_u)
{
QUrl u (_u);
// Ignore empty requests...
if (u.isEmpty())
return false;
// If the URL given is a supported local protocol, e.g. "bookmark" but lacks
// a path component, we set the path to "/" here so that the security context
// will properly allow access to local resources.
if (u.host().isEmpty() && u.path().isEmpty()
&& KProtocolInfo::protocolClass(u.scheme()) == QL1S(":local")) {
u.setPath(QL1S("/"));
}
// Do not emit update history when url is typed in since the host
// should handle that automatically itself.
m_emitOpenUrlNotify = false;
// Pointer to the page object...
WebEnginePage* p = page();
Q_ASSERT(p);
KParts::BrowserArguments bargs (m_browserExtension->browserArguments());
KParts::OpenUrlArguments args (arguments());
if (!Utils::isBlankUrl(u)) {
// Get the SSL information sent, if any...
if (args.metaData().contains(QL1S("ssl_in_use"))) {
WebSslInfo sslInfo;
sslInfo.restoreFrom(KIO::MetaData(args.metaData()).toVariant());
sslInfo.setUrl(u);
p->setSslInfo(sslInfo);
}
}
attemptInstallKIOSchemeHandler(u);
// Set URL in KParts before emitting started; konq plugins rely on that.
setUrl(u);
m_doLoadFinishedActions = true;
page()->setLoadUrlCalledByPart(u);
m_webView->loadUrl(u, args, bargs);
return true;
}
bool WebEnginePart::closeUrl()
{
m_webView->triggerPageAction(QWebEnginePage::Stop);
m_webView->stop();
return true;
}
QWebEngineView* WebEnginePart::view() const
{
return m_webView;
}
bool WebEnginePart::isModified() const
{
//return m_webView->isModified();
return false;
}
void WebEnginePart::guiActivateEvent(KParts::GUIActivateEvent *event)
{
if (event && event->activated() && m_webView) {
emit setWindowCaption(m_webView->title());
}
}
bool WebEnginePart::openFile()
{
// never reached
return false;
}
/// slots...
void WebEnginePart::slotLoadStarted()
{
if(!Utils::isBlankUrl(url()))
{
emit started(nullptr);
}
updateActions();
// If "NoEmitOpenUrlNotification" property is set to true, do not
// emit the open url notification. Property is set by this part's
// extension to prevent openUrl notification being sent when
// handling history navigation requests (back/forward).
const bool doNotEmitOpenUrl = property("NoEmitOpenUrlNotification").toBool();
if (doNotEmitOpenUrl) {
setProperty("NoEmitOpenUrlNotification", QVariant());
} else {
if (m_emitOpenUrlNotify) {
emit m_browserExtension->openUrlNotify();
}
}
// Unless we go via openUrl again, the next time we are here we emit (e.g. after clicking on a link)
m_emitOpenUrlNotify = true;
}
void WebEnginePart::slotLoadFinished (bool ok)
{
if (!ok || !m_doLoadFinishedActions)
return;
slotWalletClosed();
m_doLoadFinishedActions = false;
// If the document contains no tag, then set it to the current url.
if (m_webView->title().trimmed().isEmpty()) {
// If the document title is empty, then set it to the current url
const QUrl url (m_webView->url());
const QString caption (url.toString((QUrl::RemoveQuery|QUrl::RemoveFragment)));
emit setWindowCaption(caption);
// The urlChanged signal is emitted if and only if the main frame
// receives the title of the page so we manually invoke the slot as a
// work around here for pages that do not contain it, such as text
// documents...
slotUrlChanged(url);
}
if (!Utils::isBlankUrl(url())) {
m_hasCachedFormData = false;
if (WebEngineSettings::self()->isNonPasswordStorableSite(url().host())) {
addWalletStatusBarIcon();
}
else {
// Attempt to fill the web form...
WebEngineWallet *wallet = page() ? page()->wallet() : nullptr;
if (wallet){
wallet->fillFormData(page());
}
}
}
bool pending = false;
// QWebFrame* frame = (page() ? page()->currentFrame() : 0);
// if (ok &&
// frame == page()->mainFrame() &&
// !frame->findFirstElement(QL1S("head>meta[http-equiv=refresh]")).isNull()) {
// if (WebEngineSettings::self()->autoPageRefresh()) {
// pending = true;
// } else {
// frame->page()->triggerAction(QWebEnginePage::Stop);
// }
// }
emit completed ((ok && pending));
updateActions();
}
void WebEnginePart::slotLoadAborted(const QUrl & url)
{
closeUrl();
m_doLoadFinishedActions = false;
if (url.isValid())
emit m_browserExtension->openUrlRequest(url);
else
setUrl(m_webView->url());
}
void WebEnginePart::slotUrlChanged(const QUrl& url)
{
// Ignore if empty
if (url.isEmpty())
return;
// Ignore if error url
if (url.scheme() == QL1S("error"))
return;
const QUrl u (url);
// Ignore if url has not changed!
if (this->url() == u)
return;
m_doLoadFinishedActions = true;
setUrl(u);
// Do not update the location bar with about:blank
if (!Utils::isBlankUrl(url)) {
//kDebug() << "Setting location bar to" << u.prettyUrl() << "current URL:" << this->url();
emit m_browserExtension->setLocationBarUrl(u.toDisplayString());
}
}
void WebEnginePart::slotShowSecurity()
{
if (!page())
return;
const WebSslInfo& sslInfo = page()->sslInfo();
if (!sslInfo.isValid()) {
KMessageBox::information(nullptr, i18n("The SSL information for this site "
"appears to be corrupt."),
i18nc("Secure Sockets Layer", "SSL"));
return;
}
KSslInfoDialog *dlg = new KSslInfoDialog (widget());
dlg->setSslInfo(sslInfo.certificateChain(),
sslInfo.peerAddress().toString(),
url().host(),
sslInfo.protocol(),
sslInfo.ciphers(),
sslInfo.usedChiperBits(),
sslInfo.supportedChiperBits(),
KSslInfoDialog::errorsFromString(sslInfo.certificateErrors()));
dlg->open();
}
#if 0
void WebEnginePart::slotSaveFrameState(QWebFrame *frame, QWebHistoryItem *item)
{
if (!frame || !item) {
return;
}
// Handle actions that apply only to the mainframe...
if (frame == view()->page()->mainFrame()) {
}
// For some reason, QtWebEngine PORTING_TODO does not restore scroll position when
// QWebHistory is restored from persistent storage. Therefore, we
// preserve that information and restore it as needed. See
// slotRestoreFrameState.
const QPoint scrollPos (frame->scrollPosition());
if (!scrollPos.isNull()) {
// kDebug() << "Saving scroll position:" << scrollPos;
item->setUserData(scrollPos);
}
}
#endif
#if 0
void WebEnginePart::slotRestoreFrameState(QWebFrame *frame)
{
QWebEnginePage* page = (frame ? frame->page() : 0);
QWebHistory* history = (page ? page->history() : 0);
// No history item...
if (!history || history->count() < 1)
return;
QWebHistoryItem currentHistoryItem (history->currentItem());
// Update the scroll position if needed. See comment in slotSaveFrameState above.
if (frame->baseUrl().resolved(frame->url()) == currentHistoryItem.url()) {
const QPoint currentPos (frame->scrollPosition());
const QPoint desiredPos (currentHistoryItem.userData().toPoint());
if (currentPos.isNull() && !desiredPos.isNull()) {
frame->setScrollPosition(desiredPos);
}
}
}
#endif
void WebEnginePart::slotLinkHovered(const QString& _link)
{
QString message;
if (_link.isEmpty()) {
message = QL1S("");
emit m_browserExtension->mouseOverInfo(KFileItem());
} else {
QUrl linkUrl (_link);
const QString scheme = linkUrl.scheme();
// Protect the user against URL spoofing!
linkUrl.setUserName(QString());
const QString link (linkUrl.toString());
if (QString::compare(scheme, QL1S("mailto"), Qt::CaseInsensitive) == 0) {
message += i18nc("status bar text when hovering email links; looks like \"Email: xy@kde.org - CC: z@kde.org -BCC: x@kde.org - Subject: Hi translator\"", "Email: ");
// Workaround: for QUrl's parsing deficiencies of "mailto:foo@bar.com".
if (!linkUrl.hasQuery())
linkUrl = QUrl(scheme + '?' + linkUrl.path());
QMap fields;
QUrlQuery query(linkUrl);
QList > queryItems = query.queryItems();
const int count = queryItems.count();
for(int i = 0; i < count; ++i) {
const QPair queryItem (queryItems.at(i));
//kDebug() << "query: " << queryItem.first << queryItem.second;
if (queryItem.first.contains(QL1C('@')) && queryItem.second.isEmpty())
fields[QStringLiteral("to")] << queryItem.first;
if (QString::compare(queryItem.first, QL1S("to"), Qt::CaseInsensitive) == 0)
fields[QStringLiteral("to")] << queryItem.second;
if (QString::compare(queryItem.first, QL1S("cc"), Qt::CaseInsensitive) == 0)
fields[QStringLiteral("cc")] << queryItem.second;
if (QString::compare(queryItem.first, QL1S("bcc"), Qt::CaseInsensitive) == 0)
fields[QStringLiteral("bcc")] << queryItem.second;
if (QString::compare(queryItem.first, QL1S("subject"), Qt::CaseInsensitive) == 0)
fields[QStringLiteral("subject")] << queryItem.second;
}
if (fields.contains(QL1S("to")))
message += fields.value(QL1S("to")).join(QL1S(", "));
if (fields.contains(QL1S("cc")))
message += i18nc("status bar text when hovering email links; looks like \"Email: xy@kde.org - CC: z@kde.org -BCC: x@kde.org - Subject: Hi translator\"", " - CC: ") + fields.value(QL1S("cc")).join(QL1S(", "));
if (fields.contains(QL1S("bcc")))
message += i18nc("status bar text when hovering email links; looks like \"Email: xy@kde.org - CC: z@kde.org -BCC: x@kde.org - Subject: Hi translator\"", " - BCC: ") + fields.value(QL1S("bcc")).join(QL1S(", "));
if (fields.contains(QL1S("subject")))
message += i18nc("status bar text when hovering email links; looks like \"Email: xy@kde.org - CC: z@kde.org -BCC: x@kde.org - Subject: Hi translator\"", " - Subject: ") + fields.value(QL1S("subject")).join(QL1S(" "));
} else if (scheme == QL1S("javascript")) {
message = KStringHandler::rsqueeze(link, 150);
if (link.startsWith(QL1S("javascript:window.open")))
message += i18n(" (In new window)");
} else {
message = link;
#if 0
QWebFrame* frame = page() ? page()->currentFrame() : 0;
if (frame) {
QWebHitTestResult result = frame->hitTestContent(page()->view()->mapFromGlobal(QCursor::pos()));
QWebFrame* target = result.linkTargetFrame();
if (frame->parentFrame() && target == frame->parentFrame()) {
message += i18n(" (In parent frame)");
} else if (!target || target != frame) {
message += i18n(" (In new window)");
}
}
#endif
KFileItem item (linkUrl, QString(), KFileItem::Unknown);
emit m_browserExtension->mouseOverInfo(item);
}
}
emit setStatusBarText(message);
}
void WebEnginePart::slotSearchForText(const QString &text, bool backward)
{
QWebEnginePage::FindFlags flags; // = QWebEnginePage::FindWrapsAroundDocument;
if (backward)
flags |= QWebEnginePage::FindBackward;
if (m_searchBar->caseSensitive())
flags |= QWebEnginePage::FindCaseSensitively;
//kDebug() << "search for text:" << text << ", backward ?" << backward;
page()->findText(text, flags, [this](bool found) {
m_searchBar->setFoundMatch(found);
});
}
void WebEnginePart::slotShowSearchBar()
{
if (!m_searchBar) {
// Create the search bar...
m_searchBar = new SearchBar(widget());
connect(m_searchBar, SIGNAL(searchTextChanged(QString,bool)),
this, SLOT(slotSearchForText(QString,bool)));
actionCollection()->addAction(KStandardAction::FindNext, QStringLiteral("findnext"),
m_searchBar, SLOT(findNext()));
actionCollection()->addAction(KStandardAction::FindPrev, QStringLiteral("findprev"),
m_searchBar, SLOT(findPrevious()));
QBoxLayout* lay = qobject_cast(widget()->layout());
if (lay) {
lay->addWidget(m_searchBar);
}
}
const QString text = m_webView->selectedText();
m_searchBar->setSearchText(text.left(150));
}
void WebEnginePart::slotLinkMiddleOrCtrlClicked(const QUrl& linkUrl)
{
emit m_browserExtension->createNewWindow(linkUrl);
}
void WebEnginePart::slotSelectionClipboardUrlPasted(const QUrl& selectedUrl, const QString& searchText)
{
if (!WebEngineSettings::self()->isOpenMiddleClickEnabled())
return;
if (!searchText.isEmpty() &&
KMessageBox::questionYesNo(m_webView,
i18n("Do you want to search for %1?", searchText),
i18n("Internet Search"), KGuiItem(i18n("&Search"), QStringLiteral("edit-find")),
KStandardGuiItem::cancel(), QStringLiteral("MiddleClickSearch")) != KMessageBox::Yes)
return;
emit m_browserExtension->openUrlRequest(selectedUrl);
}
void WebEnginePart::slotWalletClosed()
{
if (!m_statusBarWalletLabel)
return;
m_statusBarExtension->removeStatusBarItem(m_statusBarWalletLabel);
delete m_statusBarWalletLabel;
m_statusBarWalletLabel = nullptr;
m_hasCachedFormData = false;
}
void WebEnginePart::slotShowWalletMenu()
{
QMenu *menu = new QMenu(nullptr);
if (m_webView && WebEngineSettings::self()->isNonPasswordStorableSite(m_webView->url().host()))
menu->addAction(i18n("&Allow password caching for this site"), this, SLOT(slotDeleteNonPasswordStorableSite()));
if (m_hasCachedFormData)
menu->addAction(i18n("Remove all cached passwords for this site"), this, SLOT(slotRemoveCachedPasswords()));
menu->addSeparator();
menu->addAction(i18n("&Close Wallet"), this, SLOT(slotWalletClosed()));
KAcceleratorManager::manage(menu);
menu->popup(QCursor::pos());
}
void WebEnginePart::slotLaunchWalletManager()
{
QDBusInterface r(QStringLiteral("org.kde.kwalletmanager"), QStringLiteral("/kwalletmanager/MainWindow_1"));
if (r.isValid())
r.call(QDBus::NoBlock, QStringLiteral("show"));
else
KToolInvocation::startServiceByDesktopName(QStringLiteral("kwalletmanager_show"));
}
void WebEnginePart::slotDeleteNonPasswordStorableSite()
{
if (m_webView)
WebEngineSettings::self()->removeNonPasswordStorableSite(m_webView->url().host());
}
void WebEnginePart::slotRemoveCachedPasswords()
{
if (!page() || !page()->wallet())
return;
page()->wallet()->removeFormData(page());
m_hasCachedFormData = false;
}
void WebEnginePart::slotSetTextEncoding(QTextCodec * codec)
{
// FIXME: The code below that sets the text encoding has been reported not to work.
if (!page())
return;
QWebEngineSettings *localSettings = page()->settings();
if (!localSettings)
return;
qCDebug(WEBENGINEPART_LOG) << "Encoding: new=>" << localSettings->defaultTextEncoding() << ", old=>" << codec->name();
localSettings->setDefaultTextEncoding(codec->name());
page()->triggerAction(QWebEnginePage::Reload);
}
void WebEnginePart::slotSetStatusBarText(const QString& text)
{
const QString host (page() ? page()->url().host() : QString());
if (WebEngineSettings::self()->windowStatusPolicy(host) == KParts::HtmlSettingsInterface::JSWindowStatusAllow)
emit setStatusBarText(text);
}
void WebEnginePart::slotWindowCloseRequested()
{
emit m_browserExtension->requestFocus(this);
#if 0
if (KMessageBox::questionYesNo(m_webView,
i18n("Close window?"), i18n("Confirmation Required"),
KStandardGuiItem::close(), KStandardGuiItem::cancel())
!= KMessageBox::Yes)
return;
#endif
this->deleteLater();
}
void WebEnginePart::slotShowFeaturePermissionBar(QWebEnginePage::Feature feature)
{
// FIXME: Allow multiple concurrent feature permission requests.
if (m_featurePermissionBar && m_featurePermissionBar->isVisible())
return;
if (!m_featurePermissionBar) {
m_featurePermissionBar = new FeaturePermissionBar(widget());
connect(m_featurePermissionBar, SIGNAL(permissionGranted(QWebEnginePage::Feature)),
this, SLOT(slotFeaturePermissionGranted(QWebEnginePage::Feature)));
connect(m_featurePermissionBar, SIGNAL(permissionDenied(QWebEnginePage::Feature)),
this, SLOT(slotFeaturePermissionDenied(QWebEnginePage::Feature)));
connect(m_passwordBar, SIGNAL(done()),
this, SLOT(slotSaveFormDataDone()));
QBoxLayout* lay = qobject_cast(widget()->layout());
if (lay)
lay->insertWidget(0, m_featurePermissionBar);
}
m_featurePermissionBar->setFeature(feature);
// m_featurePermissionBar->setText(i18n("Do you want to grant the site %1 "
// "access to information about your current physical location?",
// url.host()));
m_featurePermissionBar->setText(i18n("Do you want to grant the site "
"access to information about your current physical location?"));
m_featurePermissionBar->animatedShow();
}
void WebEnginePart::slotFeaturePermissionGranted(QWebEnginePage::Feature feature)
{
Q_ASSERT(m_featurePermissionBar && m_featurePermissionBar->feature() == feature);
page()->setFeaturePermission(page()->url(), feature, QWebEnginePage::PermissionGrantedByUser);
}
void WebEnginePart::slotFeaturePermissionDenied(QWebEnginePage::Feature feature)
{
Q_ASSERT(m_featurePermissionBar && m_featurePermissionBar->feature() == feature);
page()->setFeaturePermission(page()->url(), feature, QWebEnginePage::PermissionDeniedByUser);
}
void WebEnginePart::slotSaveFormDataRequested (const QString& key, const QUrl& url)
{
if (WebEngineSettings::self()->isNonPasswordStorableSite(url.host()))
return;
if (!WebEngineSettings::self()->askToSaveSitePassword())
return;
if (m_passwordBar && m_passwordBar->isVisible())
return;
if (!m_passwordBar) {
m_passwordBar = new PasswordBar(widget());
if (!m_wallet) {
qCWarning(WEBENGINEPART_LOG) << "No m_wallet instance found! This should never happen!";
return;
}
connect(m_passwordBar, SIGNAL(saveFormDataAccepted(QString)),
m_wallet, SLOT(acceptSaveFormDataRequest(QString)));
connect(m_passwordBar, SIGNAL(saveFormDataRejected(QString)),
m_wallet, SLOT(rejectSaveFormDataRequest(QString)));
connect(m_passwordBar, SIGNAL(done()),
this, SLOT(slotSaveFormDataDone()));
}
Q_ASSERT(m_passwordBar);
m_passwordBar->setUrl(url);
m_passwordBar->setRequestKey(key);
m_passwordBar->setText(i18n("Do you want %1 to remember the login "
"information for %2?",
QCoreApplication::applicationName(),
url.host()));
QBoxLayout* lay = qobject_cast(widget()->layout());
if (lay)
lay->insertWidget(0, m_passwordBar);
m_passwordBar->animatedShow();
}
void WebEnginePart::slotSaveFormDataDone()
{
if (!m_passwordBar)
return;
QBoxLayout* lay = qobject_cast(widget()->layout());
if (lay)
lay->removeWidget(m_passwordBar);
}
void WebEnginePart::addWalletStatusBarIcon ()
{
if (m_statusBarWalletLabel) {
m_statusBarExtension->removeStatusBarItem(m_statusBarWalletLabel);
} else {
m_statusBarWalletLabel = new KUrlLabel(m_statusBarExtension->statusBar());
m_statusBarWalletLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
m_statusBarWalletLabel->setUseCursor(false);
m_statusBarWalletLabel->setPixmap(QIcon::fromTheme(QStringLiteral("wallet-open")).pixmap(QSize(16,16)));
connect(m_statusBarWalletLabel, SIGNAL(leftClickedUrl()), SLOT(slotLaunchWalletManager()));
connect(m_statusBarWalletLabel, SIGNAL(rightClickedUrl()), SLOT(slotShowWalletMenu()));
}
m_statusBarExtension->addStatusBarItem(m_statusBarWalletLabel, 0, false);
}
void WebEnginePart::slotFillFormRequestCompleted (bool ok)
{
if ((m_hasCachedFormData = ok))
addWalletStatusBarIcon();
}
diff --git a/webenginepart/src/webenginepart.h b/webenginepart/src/webenginepart.h
index 25177eaad..f0ae8138e 100644
--- a/webenginepart/src/webenginepart.h
+++ b/webenginepart/src/webenginepart.h
@@ -1,174 +1,175 @@
/*
* This file is part of the KDE project.
*
* Copyright (C) 2007 Trolltech ASA
* Copyright (C) 2008 Urs Wolfer
* Copyright (C) 2009 Dawit Alemayehu
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
*/
#ifndef WEBENGINEPART_H
#define WEBENGINEPART_H
#include "kwebenginepartlib_export.h"
#include
#include
#include
namespace KParts {
class BrowserExtension;
class StatusBarExtension;
}
class QWebEngineView;
class WebEngineView;
class WebEnginePage;
class SearchBar;
class PasswordBar;
class FeaturePermissionBar;
class KUrlLabel;
class WebEngineBrowserExtension;
class WebEngineWallet;
class WebEngineErrorSchemeHandler;
/**
* A KPart wrapper for the QtWebEngine's browser rendering engine.
*
* This class attempts to provide the same type of integration into KPart
* plugin applications, such as Konqueror, in much the same way as KHTML.
*
* Unlink the KHTML part however, access into the internals of the rendering
* engine are provided through existing QtWebEngine class ; @see QWebEngineView.
*
*/
class KWEBENGINEPARTLIB_EXPORT WebEnginePart : public KParts::ReadOnlyPart
{
Q_OBJECT
Q_PROPERTY( bool modified READ isModified )
public:
explicit WebEnginePart(QWidget* parentWidget = nullptr, QObject* parent = nullptr,
const QByteArray& cachedHistory = QByteArray(),
const QStringList& = QStringList());
~WebEnginePart() override;
/**
* Re-implemented for internal reasons. API remains unaffected.
*
* @see KParts::ReadOnlyPart::openUrl
*/
bool openUrl(const QUrl &) override;
/**
* Re-implemented for internal reasons. API remains unaffected.
*
* @see KParts::ReadOnlyPart::closeUrl
*/
bool closeUrl() override;
/**
* Returns a pointer to the render widget used to display a web page.
*
* @see QWebEngineView.
*/
QWebEngineView *view() const;
/**
* Checks whether the page contains unsubmitted form changes.
*
* @return @p true if form changes exist.
*/
bool isModified() const;
/**
* Connects the appropriate signals from the given page to the slots
* in this class.
*/
void connectWebEnginePageSignals(WebEnginePage* page);
void slotShowFeaturePermissionBar(QWebEnginePage::Feature);
void setWallet(WebEngineWallet* wallet);
protected:
/**
* Re-implemented for internal reasons. API remains unaffected.
*
* @see KParts::ReadOnlyPart::guiActivateEvent
*/
void guiActivateEvent(KParts::GUIActivateEvent *) override;
/**
* Re-implemented for internal reasons. API remains unaffected.
*
* @see KParts::ReadOnlyPart::openFile
*/
bool openFile() override;
private Q_SLOTS:
void slotShowSecurity();
void slotShowSearchBar();
void slotLoadStarted();
void slotLoadAborted(const QUrl &);
void slotLoadFinished(bool);
void slotSearchForText(const QString &text, bool backward);
void slotLinkHovered(const QString &);
//void slotSaveFrameState(QWebFrame *frame, QWebHistoryItem *item);
//void slotRestoreFrameState(QWebFrame *frame);
void slotLinkMiddleOrCtrlClicked(const QUrl&);
void slotSelectionClipboardUrlPasted(const QUrl&, const QString&);
void slotUrlChanged(const QUrl &);
void slotWalletClosed();
void slotShowWalletMenu();
void slotLaunchWalletManager();
void slotDeleteNonPasswordStorableSite();
void slotRemoveCachedPasswords();
void slotSetTextEncoding(QTextCodec*);
void slotSetStatusBarText(const QString& text);
void slotWindowCloseRequested();
void slotSaveFormDataRequested(const QString &, const QUrl &);
void slotSaveFormDataDone();
void slotFillFormRequestCompleted(bool);
void slotFeaturePermissionGranted(QWebEnginePage::Feature);
void slotFeaturePermissionDenied(QWebEnginePage::Feature);
private:
WebEnginePage* page();
const WebEnginePage* page() const;
+ static void initWebEngineUrlSchemes();
void attemptInstallKIOSchemeHandler(const QUrl &url);
void initActions();
void updateActions();
void addWalletStatusBarIcon();
bool m_emitOpenUrlNotify;
bool m_hasCachedFormData;
bool m_doLoadFinishedActions;
KUrlLabel* m_statusBarWalletLabel;
SearchBar* m_searchBar;
PasswordBar* m_passwordBar;
FeaturePermissionBar* m_featurePermissionBar;
WebEngineBrowserExtension* m_browserExtension;
KParts::StatusBarExtension* m_statusBarExtension;
WebEngineView* m_webView;
WebEngineWallet* m_wallet;
};
#endif // WEBENGINEPART_H
diff --git a/webenginepart/src/webenginepartkiohandler.cpp b/webenginepart/src/webenginepartkiohandler.cpp
index 6463a3417..8ea049db9 100644
--- a/webenginepart/src/webenginepartkiohandler.cpp
+++ b/webenginepart/src/webenginepartkiohandler.cpp
@@ -1,108 +1,121 @@
/*
* This file is part of the KDE project.
*
* Copyright (C) 2018 Stefano Crocco
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
*/
#include "webenginepartkiohandler.h"
+
+#ifndef USE_QWEBENGINE_URL_SCHEME
#include "webengineparthtmlembedder.h"
+#endif
#include
#include
#include
+#include
#include
void WebEnginePartKIOHandler::requestStarted(QWebEngineUrlRequestJob *req)
{
m_queuedRequests << RequestJobPointer(req);
processNextRequest();
}
WebEnginePartKIOHandler::WebEnginePartKIOHandler(QObject* parent):
- QWebEngineUrlSchemeHandler(parent), m_embedder(nullptr)
+ QWebEngineUrlSchemeHandler(parent)
+#ifndef USE_QWEBENGINE_URL_SCHEME
+ , m_embedder(nullptr)
+#endif
{
connect(this, &WebEnginePartKIOHandler::ready, this, &WebEnginePartKIOHandler::sendReply);
}
void WebEnginePartKIOHandler::sendReply()
{
if (m_currentRequest) {
if (isSuccessful()) {
QBuffer *buf = new QBuffer;
buf->open(QBuffer::ReadWrite);
buf->write(m_data);
buf->seek(0);
connect(buf, &QIODevice::aboutToClose, buf, &QObject::deleteLater);
m_currentRequest->reply(m_mimeType.name().toUtf8(), buf);
} else {
m_currentRequest->fail(QWebEngineUrlRequestJob::UrlInvalid);
}
m_currentRequest.clear();
}
processNextRequest();
}
void WebEnginePartKIOHandler::processNextRequest()
{
if (m_currentRequest) {
return;
}
while (!m_currentRequest && !m_queuedRequests.isEmpty()) {
m_currentRequest = m_queuedRequests.takeFirst();
}
if (!m_currentRequest) {
return;
}
KIO::StoredTransferJob *job = KIO::storedGet(m_currentRequest ->requestUrl(), KIO::NoReload, KIO::HideProgressInfo);
connect(job, &KIO::StoredTransferJob::result, this, [this, job](){kioJobFinished(job);});
}
void WebEnginePartKIOHandler::embedderFinished(const QString& html)
{
m_data = html.toUtf8();
emit ready();
}
void WebEnginePartKIOHandler::processSlaveOutput()
{
+#ifdef USE_QWEBENGINE_URL_SCHEME
+ emit ready();
+#else
if (m_mimeType.inherits("text/html") || m_mimeType.inherits("application/xhtml+xml")) {
htmlEmbedder()->startEmbedding(m_data, m_mimeType.name());
} else {
emit ready();
}
+#endif
}
void WebEnginePartKIOHandler::kioJobFinished(KIO::StoredTransferJob* job)
{
m_error = job->error() == 0 ? QWebEngineUrlRequestJob::NoError : QWebEngineUrlRequestJob::RequestFailed;
m_errorMessage = isSuccessful() ? job->errorString() : QString();
m_data = job->data();
m_mimeType = QMimeDatabase().mimeTypeForData(m_data);
processSlaveOutput();
}
+#ifndef USE_QWEBENGINE_URL_SCHEME
WebEnginePartHtmlEmbedder * WebEnginePartKIOHandler::htmlEmbedder()
{
if (!m_embedder) {
m_embedder = new WebEnginePartHtmlEmbedder(this);
connect(htmlEmbedder(), &WebEnginePartHtmlEmbedder::finished, this, &WebEnginePartKIOHandler::embedderFinished);
}
return m_embedder;
}
+#endif
diff --git a/webenginepart/src/webenginepartkiohandler.h b/webenginepart/src/webenginepartkiohandler.h
index 9b6319d99..11e51d61b 100644
--- a/webenginepart/src/webenginepartkiohandler.h
+++ b/webenginepart/src/webenginepartkiohandler.h
@@ -1,313 +1,316 @@
/*
* This file is part of the KDE project.
*
* Copyright (C) 2018 Stefano Crocco
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
*/
#ifndef WEBENGINEPARTKIOHANDLER_H
#define WEBENGINEPARTKIOHANDLER_H
#include
#include
#include
#include
namespace KIO {
class StoredTransferJob;
};
class WebEnginePartHtmlEmbedder;
/**
* @brief Class which allows QWebEngine to access URLs provided by KIO
*
* The class assumes that the data can be obtained from KIO using `KIO::storedGet` and
* passes it to `QWebEngineUrlRequestJob::reply`. The mime type is determined using
- * `QMimeDatabase::mimeTypeForData`.If the data is HTML or XHTML,
+ * `QMimeDatabase::mimeTypeForData`. If the data is HTML or XHTML, on Qt versions before 5.12
* WebEnginePartHtmlEmbedder is used to embed the contents of local URLs inside the code,
* so that it can be displayed by QWebEngine without breaking cross-origin rules.
*
* This class provides a virtual function, processSlaveOutput() which allows to further process KIO output before
* using it as reply. Derived classes can use it to change the data, its mimetype and to set
* an error code and error message.
*
* @internal
*
* Since requestStarted() can be called multiple times, requests are stored in a list and
* processed one by one. This is hidden to derived classes, which only work with the current request.
*/
class WebEnginePartKIOHandler : public QWebEngineUrlSchemeHandler
{
Q_OBJECT
public:
/**
* Constructor
*
* @param parent the parent object
*/
WebEnginePartKIOHandler(QObject* parent);
/**
* @brief Override of `QWebEngineUrlSchemeHandler::requestStarted`
*
* It adds the new request to the list and start processing it if there aren't
* other queued requests
*
* @param req: the request job
*/
void requestStarted(QWebEngineUrlRequestJob* req) override;
protected slots:
/**
* @brief Slot called in response to the WebEnginePartHtmlEmbedder::finished() signal
*
* The base class implementation calls setData() with the resulting HTML code, then
* emits the ready() signal. You can override this function to do something else with
* the HTML code. In this case, most likely you won't want to call the base class version
* but just call setData() and emit the ready() when you're done processing the HTML code
*
* @param html: the HTML code produced by the embedder
*/
virtual void embedderFinished(const QString &html);
protected:
/**
* @brief Creates and returns the html embedder object
*
* This is the object used to replace local URLs with their content as `data` URLs.
*
* The embedder is only created on the first use of this function. When this happens, its
* WebEnginePartHtmlEmbedder::finished() signal is connected to the embedderFinished(). If you
* don't want this, either disconnect the signal or reimplement embedderFinished() so that it
* does nothing, then connect WebEnginePartHtmlEmbedder::finished() with the appropriate slot
* yourself
*
* @return the html embedder
*/
+#ifndef USE_QWEBENGINE_URL_SCHEME
WebEnginePartHtmlEmbedder* htmlEmbedder();
-
+#endif
/**
* @brief The request object
*
* @return The request object
*/
inline QPointer request() const {return m_currentRequest;}
/**
* @brief The error code to pass to `QWebEngineUrlRequestJob::fail`
*
* In the base class implementation, this can be either `QWebEngineUrlRequestJob::NoError` or
* `QWebEngineUrlRequestJob::RequestFailed`.
*
* @return the error code to pass to `QWebEngineUrlRequestJob::fail`
*/
inline QWebEngineUrlRequestJob::Error error() const {return m_error;}
/**
* @brief Sets the error code passed to `QWebEngineUrlRequestJob::fail`
*
* Changes the error code. This function can be called by derived class in their
* implementation of processSlaveOutput(). It should always be set to `QWebEngineUrlRequestJob::NoError`
* if `QWebEngineUrlRequestJob::reply` should be called.
*
* @see isSuccessful()
*
* @param error: the new error code
*/
inline void setError(QWebEngineUrlRequestJob::Error error) {m_error = error;}
/**
* @brief Whether an error has occurred or not
*
* @return `true` if error() is `QWebEngineUrlRequestJob::NoError` and false otherwise
*/
inline bool isSuccessful() const {return m_error == QWebEngineUrlRequestJob::NoError;}
/**
* @brief The error message
*
* Currently, this is only used for debugging purpose
*
* @return The error message given by `KJob::errorString` (or set by derived classes using setErrorMessage())
* or an empty string if KIO produced no error.
*/
inline QString errorMessage() const {return m_errorMessage;}
/**
* @brief Changes the error message
*
* This function can be called by derived classes from their implementation of
* processSlaveOutput().
*
* @param message the new error message. If error() is not `QWebEngineUrlRequestJob::NoError`, this
* should not be empty
*/
inline void setErrorMessage(const QString &message) {m_errorMessage = message;}
/**
* @brief The data to pass to `QWebEngineUrlRequestJob::reply`
*
* This is the data returned by the ioslave, but can be changed in processSlaveOutput() using setData()
*
* @return The data to pass to QWebEngineUrlRequestJob::reply
*/
inline QByteArray data() const {return m_data;}
/**
* @brief Changes the data to pass to `QWebEngineUrlRequestJob::reply`
*
* This function can be used by derived classes in their implementation of processSlaveOutput(). The base
* class implementation calls it for (X)HTML code to embed the content of local files in the code itself.
*
* @param data: the new data
*/
inline void setData(const QByteArray& data) {m_data = data;}
/**
* @brief The mime type of the data
*
* This value (actually, it's `name`) will be passed to QWebEngineUrlRequestJob::reply
* @return The mime type of the data
*/
inline QMimeType mimeType() const {return m_mimeType;}
/**
* @brief Changes the mime type of the data
*
* This function can be used by derived classes in their implementation of processSlaveOutput()
* if the mime type according to `QMimeDatabase::mimeTypeForData` is not correct or if they change
* the data so that its mimetype changes from what the ioslave returned.
*/
inline void setMimeType(const QMimeType &mime) {m_mimeType = mime;}
/**
* @brief Processes the output from the ioslave and replies to the request
*
* In the base class implementation, if the data is (X)HTML code (according to mimetype()), the
* htmlEmbedder() is used to embed the content of local files in the code. In response to
* WebEnginePartHtmlEmbedder::finished(), the ready() signal is emitted. For other mimetypes, the
* ready() signal is emitted directly from here.
*
* Derived classes can reimplement this function to do their own processing, of the data produced
* by the ioslave. At the beginning of this function, data(), mimetype(), and errorMessage() are
* those produced by the ioslave, while error is `QWebEngineUrlRequestJob::NoError` if the ioslave
* was successful andd `QWebEngineUrlRequestJob::RequestFailed` if there were errors. Derived classes can
* change these values using setData(), setMimeType(), setError() and setErrorMessage().
*
* It is important that, after having changed the values as desired, the ready() signal is emitted.
*/
virtual void processSlaveOutput();
signals:
/**
* @brief Signal emitted when processing of data produced by the ioslave has finished
*
* In response to this signal, sendReply() is called, sending the response to the original request
*/
void ready();
private slots:
/**
* Slot called when the ioslave finishes
*
* It reads the data, mimetype and error state from the job and stores it.
*
* @param job: the finished ioslave job
*/
void kioJobFinished(KIO::StoredTransferJob *job);
/**
* @brief Sends a reply to the `QWebEngineUrlRequestJob`
*
* The reply is sent using the values returned by data(), mimeType(), error() and errorMessage().
*
* If error() is something else from `QWebEngineUrlRequestJob::NoError`, this function calls
* `QWebEngineUrlRequestJob::fail` with the corresponding error code, otherwise it calls
* `QWebEngineUrlRequestJob::reply()` passing data() and mimetype() as arguments.
*
* This slot is called in response to the ready() signal
*/
void sendReply();
private:
/**
* @brief Starts processing the next `QWebEngineUrlRequestJob` in queue
*
* @internal
* This function removes the first (valid) request from #m_queuedRequests,
* stores it in #m_currentRequest and starts a `KIO::storedGet` for its URL
*/
void processNextRequest();
using RequestJobPointer = QPointer;
/**
* @brief A list of requests to be processed
*/
QList m_queuedRequests;
/**
* @brief The request currently being processed
*/
RequestJobPointer m_currentRequest;
/**
* @brief The error code to use for `QWebEngineUrlRequestJob::fail` for the current request
*
* This is valid only after the call to kioJobFinished()
*/
QWebEngineUrlRequestJob::Error m_error;
/**
* @brief The error message produced by KIO for the current request
*
* Currently, this is only used for debugging purposes
*
* This is valid only after the call to kioJobFinished()
*/
QString m_errorMessage;
/**
* @brief The data produced by KIO for the current request
*
* This is valid only after the call to kioJobFinished()
*/
QByteArray m_data;
/**
* @brief The mime type of the data produced by KIO for the current request
*
* This is valid only after the call to kioJobFinished()
*/
QMimeType m_mimeType;
+#ifndef USE_QWEBENGINE_URL_SCHEME
/**
* @brief The object to use for embedding `file` URLs in (X)HTML code
*
* This empty until the first time htmlEmbedder() is called
*/
WebEnginePartHtmlEmbedder *m_embedder;
+#endif
};
#endif // WEBENGINEPARTKIOHANDLER_H