SHA256
1
0
forked from pool/konqueror
konqueror/add-kwallet-support-to-webengine.patch
Luca Beltrame bedfc8b344 Accepting request 582185 from home:wolfi323:branches:KDE:Applications
- Add add-kwallet-support-to-webengine.patch to allow storing user
  names and passwords in kwallet when using the webenginepart

OBS-URL: https://build.opensuse.org/request/show/582185
OBS-URL: https://build.opensuse.org/package/show/KDE:Applications/konqueror?expand=0&rev=59
2018-03-03 23:46:03 +00:00

1257 lines
44 KiB
Diff

From 56ea2a32d2f1d9f4e6c97b27080cf8144a932ee3 Mon Sep 17 00:00:00 2001
From: Stefano Crocco <stefano.crocco@alice.it>
Date: Sun, 14 Jan 2018 11:25:17 +0100
Subject: Add back KWallet support to Konqueror
Summary:
Added back the ability to store user names and passwords to KWallet and automatically fill them when loading a page.
I copied the files `kwebwallet.cpp` and `kwebwallet.h` from the `KDEWebKit` framework and modified it to adapt to `QWebEngine`. In particular, I needed to modify the way form data is passed from javascript to C++ serializing it using JSON and read it back using `QJsonDocument`. Also, since `QWebEnginePage` doesn't allow to access frames from the C++ side, I needed to remove all the code dealing with recurisve searches and implement it in javascript.
As the old code, this intentionally doesn't work with input elements with the `autocomplete` attribute set to `false`.
I've not modified the copyright information in the files I copied from `KDEWebKit` as I don't know which is the correct way to do so: I must make clear my files are based on the work on other people but at the same time that I've changed them: how do I do this?
Test Plan: I tried to access several sites requiring a login in, asking Konqueror to save the password, then logging out and visiting the page again. Except when the `autocomplete` attribute was `false` (checked by looking at the html code), Konqueror always saved and restored login information correctly.
Reviewers: dfaure
Reviewed By: dfaure
Differential Revision: https://phabricator.kde.org/D10178
---
webenginepart/src/CMakeLists.txt | 4 +
webenginepart/src/webenginepage.cpp | 14 +-
webenginepart/src/webenginepage.h | 7 +-
webenginepart/src/webenginepart.cpp | 62 +--
webenginepart/src/webenginepart.h | 5 +
webenginepart/src/webenginepartfactory.cpp | 1 +
webenginepart/src/webenginewallet.cpp | 599 +++++++++++++++++++++++++++++
webenginepart/src/webenginewallet.h | 305 +++++++++++++++
8 files changed, 964 insertions(+), 33 deletions(-)
create mode 100644 webenginepart/src/webenginewallet.cpp
create mode 100644 webenginepart/src/webenginewallet.h
diff --git a/webenginepart/src/CMakeLists.txt b/webenginepart/src/CMakeLists.txt
index bfac067..fffce88 100644
--- a/webenginepart/src/CMakeLists.txt
+++ b/webenginepart/src/CMakeLists.txt
@@ -1,3 +1,5 @@
+find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Wallet)
+
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR})
set(kwebenginepartlib_LIB_SRCS
@@ -8,6 +10,7 @@ set(kwebenginepartlib_LIB_SRCS
websslinfo.cpp
webhistoryinterface.cpp
webenginepartdownloadmanager.cpp
+ webenginewallet.cpp
settings/webenginesettings.cpp
settings/webengine_filter.cpp
ui/searchbar.cpp
@@ -27,6 +30,7 @@ target_link_libraries(kwebenginepartlib Qt5::Core Qt5::DBus Qt5::Gui Qt5::Widget
KF5::Parts
KF5::SonnetCore
KF5::WindowSystem # for KUserTimestamp
+ KF5::Wallet
)
target_include_directories(kwebenginepartlib PUBLIC
diff --git a/webenginepart/src/webenginepage.cpp b/webenginepart/src/webenginepage.cpp
index 212e189..615e8f7 100644
--- a/webenginepart/src/webenginepage.cpp
+++ b/webenginepart/src/webenginepage.cpp
@@ -27,6 +27,7 @@
#include "webengineview.h"
#include "settings/webenginesettings.h"
#include "webenginepartdownloadmanager.h"
+#include "webenginewallet.h"
#include <QWebEngineSettings>
#include <QWebEngineProfile>
@@ -72,7 +73,8 @@ WebEnginePage::WebEnginePage(WebEnginePart *part, QWidget *parent)
m_kioErrorCode(0),
m_ignoreError(false),
m_part(part),
- m_passwdServerClient(new KPasswdServerClient)
+ m_passwdServerClient(new KPasswdServerClient),
+ m_wallet(Q_NULLPTR)
{
if (view())
WebEngineSettings::self()->computeFontSizes(view()->logicalDpiY());
@@ -94,6 +96,7 @@ WebEnginePage::WebEnginePage(WebEnginePart *part, QWidget *parent)
this->profile()->setHttpUserAgent(this->profile()->httpUserAgent() + " Konqueror (WebEnginePart)");
}
WebEnginePartDownloadManager::instance()->addPage(this);
+ m_wallet = new WebEngineWallet(this, parent ? parent->window()->winId() : 0);
}
WebEnginePage::~WebEnginePage()
@@ -204,8 +207,9 @@ bool WebEnginePage::acceptNavigationRequest(const QUrl& url, NavigationType type
bool inPageRequest = true;
switch (type) {
case QWebEnginePage::NavigationTypeFormSubmitted:
- //if (!checkFormData(request))
- // return false;
+ if (!checkFormData(url))
+ return false;
+ m_wallet->saveFormData(this);
break;
#if 0
case QWebEnginePage::NavigationTypeFormResubmitted:
@@ -548,9 +552,9 @@ bool WebEnginePage::checkLinkSecurity(const QNetworkRequest &req, NavigationType
return true;
}
-bool WebEnginePage::checkFormData(const QNetworkRequest &req) const
+bool WebEnginePage::checkFormData(const QUrl &url) const
{
- const QString scheme (req.url().scheme());
+ const QString scheme (url.scheme());
if (m_sslInfo.isValid() &&
!scheme.compare(QL1S("https")) && !scheme.compare(QL1S("mailto")) &&
diff --git a/webenginepart/src/webenginepage.h b/webenginepart/src/webenginepage.h
index 8fa0386..cb3d5ed 100644
--- a/webenginepart/src/webenginepage.h
+++ b/webenginepart/src/webenginepage.h
@@ -40,6 +40,7 @@ class WebSslInfo;
class WebEnginePart;
class QWebEngineDownloadItem;
class KPasswdServerClient;
+class WebEngineWallet;
class WebEnginePage : public QWebEnginePage
{
@@ -64,6 +65,8 @@ public:
void download(const QUrl &url, bool newWindow = false);
+ WebEngineWallet* wallet() const {return m_wallet;}
+
Q_SIGNALS:
/**
* This signal is emitted whenever a user cancels/aborts a load resource
@@ -107,7 +110,7 @@ protected Q_SLOTS:
private:
bool checkLinkSecurity(const QNetworkRequest& req, NavigationType type) const;
- bool checkFormData(const QNetworkRequest& req) const;
+ bool checkFormData(const QUrl& url) const;
bool handleMailToUrl (const QUrl& , NavigationType type) const;
void setPageJScriptPolicy(const QUrl& url);
@@ -121,6 +124,7 @@ private:
QPointer<WebEnginePart> m_part;
QScopedPointer<KPasswdServerClient> m_passwdServerClient;
+ WebEngineWallet *m_wallet;
};
@@ -158,6 +162,7 @@ private:
KParts::WindowArgs m_windowArgs;
WebWindowType m_type;
bool m_createNewWindow;
+ WebEngineWallet* m_wallet;
};
#endif // WEBENGINEPAGE_H
diff --git a/webenginepart/src/webenginepart.cpp b/webenginepart/src/webenginepart.cpp
index 10bbffc..66edce7 100644
--- a/webenginepart/src/webenginepart.cpp
+++ b/webenginepart/src/webenginepart.cpp
@@ -34,6 +34,7 @@
#include "webenginepage.h"
#include "websslinfo.h"
#include "webhistoryinterface.h"
+#include "webenginewallet.h"
#include "ui/searchbar.h"
#include "ui/passwordbar.h"
@@ -80,7 +81,8 @@ WebEnginePart::WebEnginePart(QWidget *parentWidget, QObject *parent,
m_statusBarWalletLabel(0),
m_searchBar(0),
m_passwordBar(0),
- m_featurePermissionBar(0)
+ m_featurePermissionBar(0),
+ m_wallet(Q_NULLPTR)
{
KAboutData about = KAboutData(QStringLiteral("webenginepart"),
i18nc("Program Name", "WebEnginePart"),
@@ -168,6 +170,7 @@ WebEnginePart::WebEnginePart(QWidget *parentWidget, QObject *parent,
// Load plugins once we are fully ready
loadPlugins();
+ setWallet(page()->wallet());
}
WebEnginePart::~WebEnginePart()
@@ -311,17 +314,25 @@ void WebEnginePart::connectWebEnginePageSignals(WebEnginePage* page)
m_browserExtension->setIconUrl(url);
}
});
+}
-#if 0
- KWebWallet *wallet = page->wallet();
- if (wallet) {
- connect(wallet, SIGNAL(saveFormDataRequested(QString,QUrl)),
- this, SLOT(slotSaveFormDataRequested(QString,QUrl)));
- connect(wallet, SIGNAL(fillFormRequestCompleted(bool)),
- this, SLOT(slotFillFormRequestCompleted(bool)));
- connect(wallet, SIGNAL(walletClosed()), this, SLOT(slotWalletClosed()));
+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);
}
-#endif
}
bool WebEnginePart::openUrl(const QUrl &_u)
@@ -451,15 +462,15 @@ void WebEnginePart::slotLoadFinished (bool ok)
}
if (!Utils::isBlankUrl(url())) {
m_hasCachedFormData = false;
-
if (WebEngineSettings::self()->isNonPasswordStorableSite(url().host())) {
addWalletStatusBarIcon();
- } else {
+ }
+ else {
// Attempt to fill the web form...
-// KWebWallet *webWallet = page() ? page()->wallet() : 0;
-// if (webWallet) {
-// webWallet->fillFormData(frame, false);
-// }
+ WebEngineWallet *wallet = page() ? page()->wallet() : 0;
+ if (wallet){
+ wallet->fillFormData(page());
+ }
}
}
@@ -766,10 +777,10 @@ void WebEnginePart::slotDeleteNonPasswordStorableSite()
void WebEnginePart::slotRemoveCachedPasswords()
{
- if (!page()) // || !page()->wallet())
+ if (!page() || !page()->wallet())
return;
-// page()->wallet()->removeFormData(page()->mainFrame(), true);
+ page()->wallet()->removeFormData(page());
m_hasCachedFormData = false;
}
@@ -822,8 +833,8 @@ void WebEnginePart::slotShowFeaturePermissionBar(QWebEnginePage::Feature 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()));
+ connect(m_passwordBar, SIGNAL(done()),
+ this, SLOT(slotSaveFormDataDone()));
QBoxLayout* lay = qobject_cast<QBoxLayout*>(widget()->layout());
if (lay)
lay->insertWidget(0, m_featurePermissionBar);
@@ -862,19 +873,16 @@ void WebEnginePart::slotSaveFormDataRequested (const QString& key, const QUrl& u
if (!m_passwordBar) {
m_passwordBar = new PasswordBar(widget());
-#if 0
- KWebWallet* wallet = page()->wallet();
- if (!wallet) {
- kWarning() << "No wallet instance found! This should never happen!";
+ if (!m_wallet) {
+ qDebug() << "No m_wallet instance found! This should never happen!";
return;
}
connect(m_passwordBar, SIGNAL(saveFormDataAccepted(QString)),
- wallet, SLOT(acceptSaveFormDataRequest(QString)));
+ m_wallet, SLOT(acceptSaveFormDataRequest(QString)));
connect(m_passwordBar, SIGNAL(saveFormDataRejected(QString)),
- wallet, SLOT(rejectSaveFormDataRequest(QString)));
+ m_wallet, SLOT(rejectSaveFormDataRequest(QString)));
connect(m_passwordBar, SIGNAL(done()),
this, SLOT(slotSaveFormDataDone()));
-#endif
}
Q_ASSERT(m_passwordBar);
diff --git a/webenginepart/src/webenginepart.h b/webenginepart/src/webenginepart.h
index 6889e6d..91afa42 100644
--- a/webenginepart/src/webenginepart.h
+++ b/webenginepart/src/webenginepart.h
@@ -42,6 +42,7 @@ class PasswordBar;
class FeaturePermissionBar;
class KUrlLabel;
class WebEngineBrowserExtension;
+class WebEngineWallet;
/**
* A KPart wrapper for the QtWebEngine's browser rendering engine.
@@ -98,6 +99,9 @@ public:
void connectWebEnginePageSignals(WebEnginePage* page);
void slotShowFeaturePermissionBar(QWebEnginePage::Feature);
+
+ void setWallet(WebEngineWallet* wallet);
+
protected:
/**
* Re-implemented for internal reasons. API remains unaffected.
@@ -160,6 +164,7 @@ private:
WebEngineBrowserExtension* m_browserExtension;
KParts::StatusBarExtension* m_statusBarExtension;
WebEngineView* m_webView;
+ WebEngineWallet* m_wallet;
};
#endif // WEBENGINEPART_H
diff --git a/webenginepart/src/webenginepartfactory.cpp b/webenginepart/src/webenginepartfactory.cpp
index 04853bd..7a877b4 100644
--- a/webenginepart/src/webenginepartfactory.cpp
+++ b/webenginepart/src/webenginepartfactory.cpp
@@ -24,6 +24,7 @@
#include <QWidget>
+
WebEngineFactory::~WebEngineFactory()
{
// kDebug() << this;
diff --git a/webenginepart/src/webenginewallet.cpp b/webenginepart/src/webenginewallet.cpp
new file mode 100644
index 0000000..f501b27
--- /dev/null
+++ b/webenginepart/src/webenginewallet.cpp
@@ -0,0 +1,599 @@
+/*
+ * This file is part of the KDE project.
+ *
+ * Copyright (C) 2009 Dawit Alemayehu <adawit@kde.org>
+ * Copyright (C) 2018 Stefano Crocco <stefano.crocco@alice.it>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "webenginewallet.h"
+#include "webenginepage.h"
+
+#include <KWallet>
+#include <QDebug>
+
+#include <QtCore/QSet>
+#include <QtCore/QHash>
+#include <QtCore/QFile>
+#include <QtCore/QPointer>
+#include <QtCore/QScopedPointer>
+#include <QJsonDocument>
+#include <qwindowdefs.h>
+
+#define QL1S(x) QLatin1String(x)
+#define QL1C(x) QLatin1Char(x)
+
+// Javascript used to extract/set data from <form> elements.
+static const char s_fillableFormElementExtractorJs[] = "(function(){ "
+" function findFormsRecursive(wnd, existingList, path){"
+" findFormsInFrame(wnd, existingList, path);"
+" frameList = wnd.frames;"
+" for(var i = 0; i < frameList.length; ++i) {"
+" var newPath = path.concat(i);"
+" findFormsRecursive(frameList[i], existingList, newPath);"
+" }"
+" }"
+" function findFormsInFrame(frm, existingList, path){"
+" var url = frm.location;"
+" var formList;"
+" try{ formList = frm.document.forms; } "
+" catch(e){"
+" return;"
+" }"
+" if (formList.length > 0) { "
+" for (var i = 0; i < formList.length; ++i) { "
+" var inputList = formList[i].elements; "
+" if (inputList.length < 1) { "
+" continue; "
+" } "
+" var formObject = new Object; "
+" formObject.url = url;"
+" formObject.name = formList[i].name; "
+" if (typeof(formObject.name) != 'string') { "
+" formObject.name = String(formList[i].id); "
+" } "
+" formObject.index = i; "
+" formObject.elements = new Array; "
+" for (var j = 0; j < inputList.length; ++j) { "
+" if (inputList[j].type != 'text' && inputList[j].type != 'email' && inputList[j].type != 'password') { "
+" continue; "
+" } "
+" if (inputList[j].disabled || inputList[j].autocomplete == 'off') { "
+" continue; "
+" } "
+" var element = new Object; "
+" element.name = inputList[j].name; "
+" if (typeof(element.name) != 'string' ) { "
+" element.name = String(inputList[j].id); "
+" } "
+" element.value = String(inputList[j].value); "
+" element.type = String(inputList[j].type); "
+" element.readonly = Boolean(inputList[j].readOnly); "
+" formObject.elements.push(element); "
+" } "
+" if (formObject.elements.length > 0) { "
+" formObject.framePath = path;"
+" console.log(JSON.stringify(formObject));"
+" existingList.push(JSON.stringify(formObject)); "
+" } "
+" } "
+" } "
+" }"
+" var forms = new Array;"
+" findFormsRecursive(window, forms, []);"
+" return forms;"
+"})()";
+
+//javascript used to fill a single form element
+static const char s_javascriptFillInputFragment[] = "var frm = window;"
+" for(var i=0; i < [%1].length; ++i) frm=frm.frames[i];"
+" if (frm.document.forms['%2'] && frm.document.forms['%2'].elements['%3']){"
+" frm.document.forms['%2'].elements['%3'].value='%4';\n"
+" }";
+
+
+/**
+ * Creates key used to store and retrieve form data.
+ *
+ */
+static QString walletKey(WebEngineWallet::WebForm form)
+{
+ QString key = form.url.toString(QUrl::RemoveQuery | QUrl::RemoveFragment);
+ key += QL1C('#');
+ key += form.name;
+ return key;
+}
+
+static QUrl urlForFrame(const QUrl &frameUrl, const QUrl &pageUrl)
+{
+ return (frameUrl.isEmpty() || frameUrl.isRelative() ? pageUrl.resolved(frameUrl) : frameUrl);
+}
+
+class WebEngineWallet::WebEngineWalletPrivate
+{
+public:
+ struct FormsData {
+ QPointer<WebEnginePage> page;
+ WebEngineWallet::WebFormList forms;
+ };
+
+ typedef std::function<void(const WebEngineWallet::WebFormList &)> WebWalletCallback;
+
+ WebEngineWalletPrivate(WebEngineWallet *parent);
+
+ void withFormData(WebEnginePage *page, WebWalletCallback callback, bool fillform = true, bool ignorepasswd = false);
+ WebFormList parseFormData(const QVariant &result, const QUrl &url, bool fillform = true, bool ignorepasswd = false);
+ void performFormDataParsing(const QVariant &result, const QUrl &url, WebWalletCallback callback, bool fillform, bool ignorepasswd);
+ void fillDataFromCache(WebEngineWallet::WebFormList &formList);
+ void saveDataToCache(const QString &key);
+ void removeDataFromCache(const WebFormList &formList);
+ void openWallet();
+
+ // Private slots...
+ void _k_openWalletDone(bool);
+ void _k_walletClosed();
+
+ WId wid;
+ WebEngineWallet *q;
+ QScopedPointer<KWallet::Wallet> wallet;
+ WebEngineWallet::WebFormList pendingRemoveRequests;
+ QHash<QUrl, FormsData> pendingFillRequests;
+ QHash<QString, WebEngineWallet::WebFormList> pendingSaveRequests;
+ QSet<QUrl> confirmSaveRequestOverwrites;
+};
+
+WebEngineWallet::WebEngineWalletPrivate::WebEngineWalletPrivate(WebEngineWallet *parent)
+ : wid(0), q(parent)
+{
+}
+
+WebEngineWallet::WebFormList WebEngineWallet::WebEngineWalletPrivate::parseFormData(const QVariant &result, const QUrl &url, bool fillform, bool ignorepasswd)
+{
+ const QVariantList results(result.toList());
+ WebEngineWallet::WebFormList list;
+ Q_FOREACH (const QVariant &formVariant, results) {
+ QJsonDocument doc = QJsonDocument::fromJson(formVariant.toString().toUtf8());
+ const QVariantMap map = doc.toVariant().toMap();
+ WebEngineWallet::WebForm form;
+ form.url = urlForFrame(QUrl(map[QL1S("url")].toString()), url);
+ form.name = map[QL1S("name")].toString();
+ form.index = map[QL1S("index")].toString();
+ form.framePath = map["framePath"].toStringList().join(",");
+ bool formHasPasswords = false;
+ const QVariantList elements = map[QL1S("elements")].toList();
+ QVector<WebEngineWallet::WebForm::WebField> inputFields;
+ Q_FOREACH (const QVariant &element, elements) {
+ QVariantMap elementMap(element.toMap());
+ const QString name(elementMap[QL1S("name")].toString());
+ const QString value(ignorepasswd ? QString() : elementMap[QL1S("value")].toString());
+ if (name.isEmpty()) {
+ continue;
+ }
+ if (fillform && elementMap[QL1S("readonly")].toBool()) {
+ continue;
+ }
+ if (elementMap[QL1S("type")].toString().compare(QL1S("password"), Qt::CaseInsensitive) == 0) {
+ if (!fillform && value.isEmpty()) {
+ continue;
+ }
+ formHasPasswords = true;
+ }
+ inputFields.append(qMakePair(name, value));
+ }
+ // Only add the input fields on form save requests...
+ if (formHasPasswords || fillform) {
+ form.fields = inputFields;
+ }
+
+ // Add the form to the list if we are saving it or it has cached data.
+ if ((fillform && q->hasCachedFormData(form)) || (!fillform && !form.fields.isEmpty())) {
+ list << form;
+ }
+ }
+ return list;
+}
+
+void WebEngineWallet::WebEngineWalletPrivate::withFormData(WebEnginePage* page, WebWalletCallback callback, bool fillform, bool ignorepasswd)
+{
+ Q_ASSERT(page);
+ QUrl url = page->url();
+ auto internalCallback = [this, url, fillform, ignorepasswd, callback](const QVariant &result){
+ WebFormList res = parseFormData(result, url, fillform, ignorepasswd);
+ callback(res);
+ };
+ page->runJavaScript(QL1S(s_fillableFormElementExtractorJs), internalCallback);
+}
+
+void WebEngineWallet::WebEngineWalletPrivate::fillDataFromCache(WebEngineWallet::WebFormList &formList)
+{
+ if (!wallet) {
+ qWarning() << "Unable to retrieve form data from wallet";
+ return;
+ }
+
+ QString lastKey;
+ QMap<QString, QString> cachedValues;
+ QMutableVectorIterator <WebForm> formIt(formList);
+
+ while (formIt.hasNext()) {
+ WebEngineWallet::WebForm &form = formIt.next();
+ const QString key(walletKey(form));
+ if (key != lastKey && wallet->readMap(key, cachedValues) != 0) {
+ qWarning() << "Unable to read form data for key:" << key;
+ continue;
+ }
+
+ for (int i = 0, count = form.fields.count(); i < count; ++i) {
+ form.fields[i].second = cachedValues.value(form.fields[i].first);
+ }
+ lastKey = key;
+ }
+}
+
+void WebEngineWallet::WebEngineWalletPrivate::saveDataToCache(const QString &key)
+{
+ // Make sure the specified keys exists before acting on it. See BR# 270209.
+ if (!pendingSaveRequests.contains(key)) {
+ return;
+ }
+
+ bool success = false;
+ const QUrl url = pendingSaveRequests.value(key).first().url;
+
+ if (wallet) {
+ int count = 0;
+ const WebEngineWallet::WebFormList list = pendingSaveRequests.value(key);
+ QVectorIterator<WebEngineWallet::WebForm> formIt(list);
+
+ while (formIt.hasNext()) {
+ QMap<QString, QString> values, storedValues;
+ const WebEngineWallet::WebForm form = formIt.next();
+ const QString accessKey = walletKey(form);
+ if (confirmSaveRequestOverwrites.contains(url)) {
+ confirmSaveRequestOverwrites.remove(url);
+ const int status = wallet->readMap(accessKey, storedValues);
+ if (status == 0 && storedValues.count()) {
+ QVectorIterator<WebEngineWallet::WebForm::WebField> fieldIt(form.fields);
+ while (fieldIt.hasNext()) {
+ const WebEngineWallet::WebForm::WebField field = fieldIt.next();
+ if (storedValues.contains(field.first) &&
+ storedValues.value(field.first) != field.second) {
+ emit q->saveFormDataRequested(key, url);
+ return;
+ }
+ }
+ // If we got here it means the new credential is exactly
+ // the same as the one already cached ; so skip the
+ // re-saving part...
+ success = true;
+ continue;
+ }
+ }
+ QVectorIterator<WebEngineWallet::WebForm::WebField> fieldIt(form.fields);
+ while (fieldIt.hasNext()) {
+ const WebEngineWallet::WebForm::WebField field = fieldIt.next();
+ values.insert(field.first, field.second);
+ }
+
+ if (wallet->writeMap(accessKey, values) == 0) {
+ count++;
+ } else {
+ qWarning() << "Unable to write form data to wallet";
+ }
+ }
+
+ if (list.isEmpty() || count > 0) {
+ success = true;
+ }
+
+ pendingSaveRequests.remove(key);
+ } else {
+ qWarning() << "NULL Wallet instance!";
+ }
+
+ emit q->saveFormDataCompleted(url, success);
+}
+
+void WebEngineWallet::WebEngineWalletPrivate::openWallet()
+{
+ if (!wallet.isNull()) {
+ return;
+ }
+
+ wallet.reset(KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(),
+ wid, KWallet::Wallet::Asynchronous));
+
+ if (wallet.isNull()) {
+ return;
+ }
+
+ connect(wallet.data(), SIGNAL(walletOpened(bool)), q, SLOT(_k_openWalletDone(bool)));
+ connect(wallet.data(), SIGNAL(walletClosed()), q, SLOT(_k_walletClosed()));
+}
+
+void WebEngineWallet::WebEngineWalletPrivate::removeDataFromCache(const WebFormList &formList)
+{
+ if (!wallet) {
+ qWarning() << "NULL Wallet instance!";
+ return;
+ }
+
+ QVectorIterator<WebForm> formIt(formList);
+ while (formIt.hasNext()) {
+ wallet->removeEntry(walletKey(formIt.next()));
+ }
+}
+
+void WebEngineWallet::WebEngineWalletPrivate::_k_openWalletDone(bool ok)
+{
+ Q_ASSERT(wallet);
+
+ if (ok &&
+ (wallet->hasFolder(KWallet::Wallet::FormDataFolder()) ||
+ wallet->createFolder(KWallet::Wallet::FormDataFolder())) &&
+ wallet->setFolder(KWallet::Wallet::FormDataFolder())) {
+
+ // Do pending fill requests...
+ if (!pendingFillRequests.isEmpty()) {
+ QList<QUrl> urlList;
+ QMutableHashIterator<QUrl, FormsData> requestIt(pendingFillRequests);
+ while (requestIt.hasNext()) {
+ requestIt.next();
+ WebEngineWallet::WebFormList list = requestIt.value().forms;
+ fillDataFromCache(list);
+ q->fillWebForm(requestIt.key(), list);
+ }
+
+ pendingFillRequests.clear();
+ }
+
+ // Do pending save requests...
+ if (!pendingSaveRequests.isEmpty()) {
+ QListIterator<QString> keysIt(pendingSaveRequests.keys());
+ while (keysIt.hasNext()) {
+ saveDataToCache(keysIt.next());
+ }
+ }
+
+ // Do pending remove requests...
+ if (!pendingRemoveRequests.isEmpty()) {
+ removeDataFromCache(pendingRemoveRequests);
+ pendingRemoveRequests.clear();
+ }
+ } else {
+ // Delete the wallet if opening the wallet failed or we were unable
+ // to change to the folder we wanted to change to.
+ delete wallet.take();
+ }
+}
+
+void WebEngineWallet::WebEngineWalletPrivate::_k_walletClosed()
+{
+ if (wallet) {
+ wallet.take()->deleteLater();
+ }
+
+ emit q->walletClosed();
+}
+
+WebEngineWallet::WebEngineWallet(QObject *parent, WId wid)
+ : QObject(parent), d(new WebEngineWalletPrivate(this))
+{
+ d->wid = wid;
+}
+
+WebEngineWallet::~WebEngineWallet()
+{
+ delete d;
+}
+
+void WebEngineWallet::fillFormDataCallback(WebEnginePage* page, const WebEngineWallet::WebFormList& formsList)
+{
+ QList<QUrl> urlList;
+ if (!formsList.isEmpty()) {
+ const QUrl url(page->url());
+ if (d->pendingFillRequests.contains(url)) {
+ qWarning() << "Duplicate request rejected!";
+ } else {
+ WebEngineWalletPrivate::FormsData data;
+ data.page = QPointer<WebEnginePage>(page);
+ data.forms << formsList;
+ d->pendingFillRequests.insert(url, data);
+ urlList << url;
+ }
+ }
+
+ if (!urlList.isEmpty()) {
+ fillFormDataFromCache(urlList);
+ }
+
+}
+
+void WebEngineWallet::fillFormData(WebEnginePage *page)
+{
+ if (!page) return;
+ auto callback = [this, page](const WebFormList &forms){
+ fillFormDataCallback(page, forms);
+ };
+ d->withFormData(page, callback);
+}
+
+static void createSaveKeyFor(WebEnginePage *page, QString *key)
+{
+ QUrl pageUrl(page->url());
+ pageUrl.setPassword(QString());
+
+ QString keyStr = pageUrl.toString();
+
+ *key = QString::number(qHash(keyStr), 16);
+}
+
+void WebEngineWallet::saveFormData(WebEnginePage *page, bool ignorePasswordFields)
+{
+ if (!page) {
+ return;
+ }
+
+ QString key;
+ createSaveKeyFor(page, &key);
+ if (d->pendingSaveRequests.contains(key)) {
+ return;
+ }
+
+ QUrl url = page->url();
+ auto callback = [this, key, url](const WebFormList &list){saveFormDataCallback(key, url, list);};
+ d->withFormData(page, callback, false, ignorePasswordFields);
+}
+
+void WebEngineWallet::saveFormDataCallback(const QString &key, const QUrl& url, const WebEngineWallet::WebFormList& formsList)
+{
+
+ if (formsList.isEmpty()) {
+ return;
+ }
+
+ WebFormList list(formsList);
+
+ d->pendingSaveRequests.insert(key, list);
+
+ QMutableVectorIterator<WebForm> it(list);
+ while (it.hasNext()) {
+ const WebForm form(it.next());
+ if (hasCachedFormData(form)) {
+ it.remove();
+ }
+ }
+
+ if (list.isEmpty()) {
+ d->confirmSaveRequestOverwrites.insert(url);
+ saveFormDataToCache(key);
+ return;
+ }
+
+ emit saveFormDataRequested(key, url);
+}
+
+void WebEngineWallet::removeFormData(WebEnginePage *page)
+{
+ if (page) {
+ auto callback = [this](const WebFormList &list){removeFormDataFromCache(list);};
+ d->withFormData(page, callback);
+ }
+}
+
+void WebEngineWallet::removeFormDataCallback(const WebFormList& list)
+{
+ removeFormDataFromCache(list);
+}
+
+
+void WebEngineWallet::removeFormData(const WebFormList &forms)
+{
+ d->pendingRemoveRequests << forms;
+ removeFormDataFromCache(forms);
+}
+
+void WebEngineWallet::acceptSaveFormDataRequest(const QString &key)
+{
+ saveFormDataToCache(key);
+}
+
+void WebEngineWallet::rejectSaveFormDataRequest(const QString &key)
+{
+ d->pendingSaveRequests.remove(key);
+}
+
+void WebEngineWallet::fillWebForm(const QUrl &url, const WebEngineWallet::WebFormList &forms)
+{
+ QPointer<WebEnginePage> page = d->pendingFillRequests.value(url).page;
+ if (!page) {
+ return;
+ }
+
+ QString script;
+ bool wasFilled = false;
+
+ Q_FOREACH (const WebEngineWallet::WebForm &form, forms) {
+ Q_FOREACH (const WebEngineWallet::WebForm::WebField &field, form.fields) {
+ QString value = field.second;
+ value.replace(QL1C('\\'), QL1S("\\\\"));
+ script+= QString(s_javascriptFillInputFragment)
+ .arg(form.framePath)
+ .arg((form.name.isEmpty() ? form.index : form.name))
+ .arg(field.first).arg(value);
+ }
+ }
+ if (!script.isEmpty()) {
+ wasFilled = true;
+ auto callback = [wasFilled, this](const QVariant &){emit fillFormRequestCompleted(wasFilled);};
+ page.data()->runJavaScript(script, callback);
+ }
+}
+
+WebEngineWallet::WebFormList WebEngineWallet::formsToFill(const QUrl &url) const
+{
+ return d->pendingFillRequests.value(url).forms;
+}
+
+WebEngineWallet::WebFormList WebEngineWallet::formsToSave(const QString &key) const
+{
+ return d->pendingSaveRequests.value(key);
+}
+
+bool WebEngineWallet::hasCachedFormData(const WebForm &form) const
+{
+ return !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
+ KWallet::Wallet::FormDataFolder(),
+ walletKey(form));
+}
+
+void WebEngineWallet::fillFormDataFromCache(const QList<QUrl> &urlList)
+{
+ if (d->wallet) {
+ QListIterator<QUrl> urlIt(urlList);
+ while (urlIt.hasNext()) {
+ const QUrl url = urlIt.next();
+ WebFormList list = formsToFill(url);
+ d->fillDataFromCache(list);
+ fillWebForm(url, list);
+ }
+ d->pendingFillRequests.clear();
+ }
+ d->openWallet();
+}
+
+void WebEngineWallet::saveFormDataToCache(const QString &key)
+{
+ if (d->wallet) {
+ d->saveDataToCache(key);
+ return;
+ }
+ d->openWallet();
+}
+
+void WebEngineWallet::removeFormDataFromCache(const WebFormList &forms)
+{
+ if (d->wallet) {
+ d->removeDataFromCache(forms);
+ d->pendingRemoveRequests.clear();
+ return;
+ }
+ d->openWallet();
+}
+
+#include "moc_webenginewallet.cpp"
diff --git a/webenginepart/src/webenginewallet.h b/webenginepart/src/webenginewallet.h
new file mode 100644
index 0000000..2c3d65b
--- /dev/null
+++ b/webenginepart/src/webenginewallet.h
@@ -0,0 +1,305 @@
+/*
+ * This file is part of the KDE project.
+ *
+ * Copyright (C) 2009 Dawit Alemayehu <adawit@kde.org>
+ * Copyright (C) 2018 Stefano Crocco <stefano.crocco@alice.it>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef WEBENGINEWALLET_H
+#define WEBENGINEWALLET_H
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+#include <QtCore/QList>
+#include <QtCore/QPair>
+#include <QUrl>
+#include <QWidget>
+#include <QtCore/QtGlobal>
+
+class WebEnginePage;
+
+class WebEngineWallet : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Holds data from a HTML &lt;form&gt; element.
+ */
+ struct WebForm {
+ /**
+ * A typedef for storing the name and value attributes of HTML &lt;input&gt;
+ * elements.
+ */
+ typedef QPair<QString, QString> WebField;
+
+ /** The URL the form was found at. */
+ QUrl url;
+ /** The name attribute of the form. */
+ QString name;
+ /** The position of the form on the web page, relative to other forms. */
+ QString index;
+ /** The path of the frame the form belongs to relative to the toplevel window (in the javascript sense).
+ *
+ * This is stored as a string containing a javascript array (it is passed as is to javascript code, so no need to store it in C++ format
+ */
+ QString framePath;
+ /** The name and value attributes of each input element in the form. */
+ QVector<WebField> fields;
+ };
+
+ /**
+ * A list of web forms
+ */
+ typedef QVector<WebForm> WebFormList;
+
+ /**
+ * Constructs a WebEngineWebWallet
+ *
+ * @p parent is usually the WebEnginePage this wallet is being used for.
+ *
+ * The @p wid parameter is used to tell the KWallet manager which window
+ * is requesting access to the wallet.
+ *
+ * @param parent the owner of this wallet
+ * @param wid the window ID of the window the web page will be
+ * embedded in
+ */
+ explicit WebEngineWallet(QObject *parent = nullptr, WId wid = 0);
+
+ /**
+ * Destructor
+ */
+ virtual ~WebEngineWallet();
+
+ /**
+ * Attempts to save the form data from @p page and its children frames.
+ *
+ * You must connect to the @ref saveFormDataRequested signal and call either
+ * @ref rejectSaveFormDataRequest or @ref acceptSaveFormDataRequest signals
+ * in order to complete the save request. Otherwise, you request will simply
+ * be ignored.
+ *
+ * Note that this function is asynchronous, as it requires running javascript code
+ * on the page using @ref QWebEnginePage::runJavaScript. This function only requests
+ * for the form data to be saved when @ref QWebEnginePage::runJavaScript finishes.
+ * The actual saving is done by @ref saveFormDataCallback
+ */
+ void saveFormData(WebEnginePage *page, bool ignorePasswordFields = false);
+
+ /**
+ * Attempts to fill forms contained in @p page with cached data.
+ *
+ * Note that this function is asynchronous, as it requires running javascript code
+ * on the page using @ref QWebEnginePage::runJavaScript. This function only requests
+ * for the form data to be filled when @ref QWebEnginePage::runJavaScript finishes.
+ * The actual filling is done by @ref fillFormDataCallback
+ */
+ void fillFormData(WebEnginePage *page);
+
+ /**
+ * Removes the form data specified by @p forms from the persistent storage.
+ *
+ * Note that this function will remove all cached data for forms found in @p page.
+ *
+ * Note that this function is asynchronous, as it requires running javascript code
+ * on the page using @ref QWebEnginePage::runJavaScript. This function only requests
+ * for the form data to be removed when @ref QWebEnginePage::runJavaScript finishes.
+ * The actual removing is done by @ref removeFormDataCallback
+ */
+ void removeFormData(WebEnginePage *page);
+
+ /**
+ * Removes the form data specified by @p forms from the persistent storage.
+ *
+ * @see formsWithCachedData
+ */
+ void removeFormData(const WebFormList &forms);
+
+public Q_SLOTS:
+ /**
+ * Accepts the save form data request associated with @p key.
+ *
+ * The @p key parameter is the one sent through the @ref saveFormDataRequested
+ * signal.
+ *
+ * You must always call this function or @ref rejectSaveFormDataRequest in
+ * order to complete the save form data request. Otherwise, the request will
+ * simply be ignored.
+ *
+ * @see saveFormDataRequested.
+ */
+ void acceptSaveFormDataRequest(const QString &key);
+
+ /**
+ * Rejects the save form data request associated with @p key.
+ *
+ * The @p key parameter is the one sent through the @ref saveFormDataRequested
+ * signal.
+ *
+ * @see saveFormDataRequested.
+ */
+ void rejectSaveFormDataRequest(const QString &key);
+
+Q_SIGNALS:
+ /**
+ * This signal is emitted whenever a save form data request is received.
+ *
+ * Unless you connect to this signal and and call @ref acceptSaveFormDataRequest
+ * or @ref rejectSaveFormDataRequest slots, the save form data requested through
+ * @ref saveFormData will simply be ignored.
+ *
+ * @p key is a value that uniquely identifies the save request and @p url
+ * is the address for which the form data is being saved.
+ *
+ * @see acceptSaveFormDataRequest
+ * @see rejectSaveFormDataRequest
+ */
+ void saveFormDataRequested(const QString &key, const QUrl &url);
+
+ /**
+ * This signal is emitted whenever a save form data request is completed.
+ *
+ * @p ok will be set to true if the save form data request for @p url was
+ * completed successfully.
+ *
+ * @see saveFormDataRequested
+ */
+ void saveFormDataCompleted(const QUrl &url, bool ok);
+
+ /**
+ * This signal is emitted whenever a fill form data request is completed.
+ *
+ * @p ok will be set to true if any forms were successfully filled with
+ * cached data from the persistent storage.
+ *
+ * @see fillFormData
+ * @since 4.5
+ */
+ void fillFormRequestCompleted(bool ok);
+
+ /**
+ * This signal is emitted whenever the current wallet is closed.
+ */
+ void walletClosed();
+
+protected:
+ /**
+ * Returns a list of forms for @p url that are waiting to be filled.
+ *
+ * This function returns an empty list if there is no pending requests
+ * for filling forms associated with @p url.
+ */
+ WebFormList formsToFill(const QUrl &url) const;
+
+ /**
+ * Returns a list of for @p key that are waiting to be saved.
+ *
+ * This function returns an empty list if there are no pending requests
+ * for saving forms associated with @p key.
+ */
+ WebFormList formsToSave(const QString &key) const;
+
+ /**
+ * Returns forms to be removed from persistent storage.
+ */
+ WebFormList formsToDelete() const;
+
+ /**
+ * Returns true when there is data associated with @p form in the
+ * persistent storage.
+ */
+ bool hasCachedFormData(const WebForm &form) const;
+
+ /**
+ * Fills the web forms in frame that point to @p url with data from @p forms.
+ *
+ * @see fillFormDataFromCache.
+ */
+ void fillWebForm(const QUrl &url, const WebFormList &forms);
+
+ /**
+ * Fills form data from persistent storage.
+ *
+ * If you reimplement this function, call @ref formsToFill to obtain
+ * the list of forms pending to be filled. Once you fill the list with
+ * the cached data from the persistent storage, you must call @p fillWebForm
+ * to fill out the actual web forms.
+ *
+ * @see formsToFill
+ */
+ void fillFormDataFromCache(const QList<QUrl> &list);
+
+ /**
+ * Stores form data associated with @p key to a persistent storage.
+ *
+ * If you reimplement this function, call @ref formsToSave to obtain the
+ * list of form data pending to be saved to persistent storage.
+ *
+ *@see formsToSave
+ */
+ void saveFormDataToCache(const QString &key);
+
+ /**
+ * Removes all cached form data associated with @p forms from persistent storage.
+ *
+ * If you reimplement this function, call @ref formsToDelete to obtain the
+ * list of form data pending to be removed from persistent storage.
+ *
+ *@see formsToDelete
+ */
+ void removeFormDataFromCache(const WebFormList &forms);
+
+private:
+
+ /**
+ * Callback used by @ref fillFormData to insert form data
+ *
+ * This function is called as a callback from @ref fillFormData after
+ * @ref QWebEnginePage::runJavaScript has finished
+ */
+ void fillFormDataCallback(WebEnginePage *page, const WebFormList &formsList);
+
+ /**
+ * Callback used by @ref saveFormData to save data
+ *
+ * This function is called as a callback from @ref saveFormData after
+ * @ref QWebEnginePage::runJavaScript has finished
+ */
+ void saveFormDataCallback(const QString &key, const QUrl &url, const WebFormList &formslist);
+
+ /**
+ * Callback used by @ref removeFormData to remove data
+ *
+ * This function is called as a callback from @ref removeFormData after
+ * @ref QWebEnginePage::runJavaScript has finished
+ */
+ void removeFormDataCallback(const WebFormList &list);
+
+private:
+ class WebEngineWalletPrivate;
+ friend class WebEngineWalletPrivate;
+ WebEngineWalletPrivate *const d;
+
+ Q_PRIVATE_SLOT(d, void _k_openWalletDone(bool))
+ Q_PRIVATE_SLOT(d, void _k_walletClosed())
+};
+
+
+#endif // WEBENGINEWALLET_H
--
cgit v0.11.2