diff --git a/webenginepart/src/webenginepartkiohandler.h b/webenginepart/src/webenginepartkiohandler.h index af67f77eb..9b6319d99 100644 --- a/webenginepart/src/webenginepartkiohandler.h +++ b/webenginepart/src/webenginepartkiohandler.h @@ -1,313 +1,313 @@ /* * 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, * 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 */ WebEnginePartHtmlEmbedder* htmlEmbedder(); /** * @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;} + 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; /** * @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 // WEBENGINEPARTKIOHANDLER_H diff --git a/webenginepart/src/webenginewallet.cpp b/webenginepart/src/webenginewallet.cpp index df5763027..ab67893cb 100644 --- a/webenginepart/src/webenginewallet.cpp +++ b/webenginepart/src/webenginewallet.cpp @@ -1,599 +1,599 @@ /* * This file is part of the KDE project. * * Copyright (C) 2009 Dawit Alemayehu * Copyright (C) 2018 Stefano Crocco * * 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 #include #include #include #include #include #include #include #include #define QL1S(x) QLatin1String(x) #define QL1C(x) QLatin1Char(x) // Javascript used to extract/set data from
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) +static QString walletKey(const 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 page; WebEngineWallet::WebFormList forms; }; typedef std::function WebWalletCallback; WebEngineWalletPrivate(WebEngineWallet *parent); - void withFormData(WebEnginePage *page, WebWalletCallback callback, bool fillform = true, bool ignorepasswd = false); + void withFormData(WebEnginePage *page, const 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 wallet; WebEngineWallet::WebFormList pendingRemoveRequests; QHash pendingFillRequests; QHash pendingSaveRequests; QSet 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 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) +void WebEngineWallet::WebEngineWalletPrivate::withFormData(WebEnginePage* page, const 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) { qCWarning(WEBENGINEPART_LOG) << "Unable to retrieve form data from wallet"; return; } QString lastKey; QMap cachedValues; QMutableVectorIterator formIt(formList); while (formIt.hasNext()) { WebEngineWallet::WebForm &form = formIt.next(); const QString key(walletKey(form)); if (key != lastKey && wallet->readMap(key, cachedValues) != 0) { qCWarning(WEBENGINEPART_LOG) << "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 formIt(list); while (formIt.hasNext()) { QMap 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 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 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 { qCWarning(WEBENGINEPART_LOG) << "Unable to write form data to wallet"; } } if (list.isEmpty() || count > 0) { success = true; } pendingSaveRequests.remove(key); } else { qCWarning(WEBENGINEPART_LOG) << "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) { qCWarning(WEBENGINEPART_LOG) << "NULL Wallet instance!"; return; } QVectorIterator 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 urlList; QMutableHashIterator 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 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 urlList; if (!formsList.isEmpty()) { const QUrl url(page->url()); if (d->pendingFillRequests.contains(url)) { qCWarning(WEBENGINEPART_LOG) << "Duplicate request rejected!"; } else { WebEngineWalletPrivate::FormsData data; data.page = QPointer(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 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 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 &urlList) { if (d->wallet) { QListIterator 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"