diff --git a/webenginepart/src/CMakeLists.txt b/webenginepart/src/CMakeLists.txt --- a/webenginepart/src/CMakeLists.txt +++ b/webenginepart/src/CMakeLists.txt @@ -15,7 +15,8 @@ webenginepartdownloadmanager.cpp webenginewallet.cpp webengineparterrorschemehandler.cpp - webengineparthtmlmimetypehandler.cpp + webengineparthtmlembedder.cpp + webenginepartkiohandler.cpp settings/webenginesettings.cpp settings/webengine_filter.cpp ui/searchbar.cpp diff --git a/webenginepart/src/webenginepart.cpp b/webenginepart/src/webenginepart.cpp --- a/webenginepart/src/webenginepart.cpp +++ b/webenginepart/src/webenginepart.cpp @@ -23,7 +23,7 @@ */ #include "webenginepart.h" -#include "webengineparthtmlmimetypehandler.h" +#include "webenginepartkiohandler.h" //#include #include @@ -91,6 +91,7 @@ QWebEngineProfile *prof = QWebEngineProfile::defaultProfile(); if (!prof->urlSchemeHandler("error")) { prof->installUrlSchemeHandler("error", new WebEnginePartErrorSchemeHandler(prof)); + prof->installUrlSchemeHandler("help", new WebEnginePartKIOHandler(prof)); } KAboutData about = KAboutData(QStringLiteral("webenginepart"), i18nc("Program Name", "WebEnginePart"), @@ -349,7 +350,7 @@ QWebEngineProfile *prof = QWebEngineProfile::defaultProfile(); QByteArray scheme = url.scheme().toUtf8(); if (!prof->urlSchemeHandler(scheme)) { - prof->installUrlSchemeHandler(scheme, new WebEnginePartHtmlMimetypeHandler(prof)); + prof->installUrlSchemeHandler(scheme, new WebEnginePartKIOHandler(prof)); } } diff --git a/webenginepart/src/webengineparthtmlmimetypehandler.h b/webenginepart/src/webengineparthtmlembedder.h rename from webenginepart/src/webengineparthtmlmimetypehandler.h rename to webenginepart/src/webengineparthtmlembedder.h --- a/webenginepart/src/webengineparthtmlmimetypehandler.h +++ b/webenginepart/src/webengineparthtmlembedder.h @@ -22,41 +22,46 @@ * */ -#ifndef WEBENGINEPARTHTMLMIMETYPEHANDLER_H -#define WEBENGINEPARTHTMLMIMETYPEHANDLER_H +#ifndef WEBENGINEPARTHTMLEMBEDDER_H +#define WEBENGINEPARTHTMLEMBEDDER_H #include "utils.h" -#include -#include +#include -class QWebEngineUrlRequestJob; class QWebEnginePage; +class QWebEngineProfile; /** - * @brief Url scheme handler for URLs for which KIO produces HTML output + * @brief Class which embeds content from local files referenced in (X)HTML code inside the + * page itself using `data` URLs * - * This class replies to the request with the HTML code produced by KIO. + * This class works asynchronously: startEmbedding() starts the embedding process and the finished() + * signal is emitted when the embedding has finished with the resulting (X)HTML code as argument. * * @internal * - * The issue is that html contains references to local resources (CSS, images) - * and QtWebEngine refuses to load them because of cross-origin rules - * (URLs with scheme other than `file` aren't considered local and there's no way - * to change this). To solve this problem, this class parses the HTML generated by KIO - * using `QWebEnginePage`, then replaces URLs pointing to local files with their content - * embedded as `data` URLs. Currently, this is only done for `link` (stylesheet) and `img` elements. + * This class parses the HTML generated by KIO using `QWebEnginePage`, then replaces URLs pointing + * to local files with their content embedded as `data` URLs. Currently, this is only done for + * `link` (stylesheet) and `img` elements. * * The main complications with this approach is that the to query the `QWebEnginePage` for * the HTML elements it contains one must use javascript and that the related functions * are all asynchronous. For this reason, we must rely on signals to tell when each step is done: * * `QWebEnginePage::setHtml` -> `QWebEnginePage::loadFinished` -> startExtractingUrls() -> urlsExtracted() -> * startReplacingUrls() -> urlsReplaced() -> startRetrievingHtml() -> htmlRetrieved() -> sendReply() + * + * A problem can arise if the (X)HTML code contains a `` element with attribute `http-equiv="refresh"`. + * Since QWebEnginePage its not just a parser, it honour this refresh request and, after emitting the `loadFinished` + * signal, it refreshes the page, which means that `loadFinished` is emitted again and again. To avoid + * doing the embeddding every time, the `loadFinished` signal is disconnected after the first time it's + * emitted and the connection is remade the next time startEmbedding() is called. * - * @note The `QWebEnginePage` used here is different from the one associated with the WebEnginePart. + * @note This class uses and internal `QWebEnginePage` and an associated, off the record, `QWebEngineProfile` + * so as not interfere with the rest of the part */ -class WebEnginePartHtmlMimetypeHandler : public QWebEngineUrlSchemeHandler +class WebEnginePartHtmlEmbedder : public QObject { Q_OBJECT @@ -67,19 +72,22 @@ * * @param parent the parent object */ - WebEnginePartHtmlMimetypeHandler(QObject* parent = Q_NULLPTR); + WebEnginePartHtmlEmbedder(QObject* parent = Q_NULLPTR); /** * @brief Destructor */ - ~WebEnginePartHtmlMimetypeHandler(){} + ~WebEnginePartHtmlEmbedder(){} /** - * Implementation of `QWebEngineUrlSchemeHandler::requestStarted` - * - * @param job the information about the request - */ - void requestStarted(QWebEngineUrlRequestJob* job) Q_DECL_OVERRIDE; + * @brief Starts the embedding process asynchronously + * + * The finished() signal is emitted when the HTML with embedded URLs is ready + * + * @param html: the HTML code + * @param mimeType: the mime type (to distinguish between HTML and XHTML) + */ + void startEmbedding(const QByteArray &html, const QString &mimeType); signals: @@ -96,12 +104,14 @@ void urlsReplaced(); /** - * @brief Signal emitted when the `QWebEnginePage` has returned its HTML code + * @brief Signal emitted when the HTML code with the embedded data is ready + * + * Users of this class should connect to this signal to retrieve the HTML code. * * @param html the HTML code of the page */ - void htmlRetrieved(const QString &html); - + void finished(const QString &html); + private slots: /** @@ -124,17 +134,6 @@ */ void startReplacingUrls(const QStringList &urls); - /** - * @brief Sends the reply to the original URL request - * - * This function assumes that `html` contains HTML (rather than XHTML) code. - * - * This function is called in response to the htmlRetrieved() signal - * - * @param html the HTML code to be sent as reply - */ - void sendReply(const QString &html); - /** * @brief Calls the `QWebEnginePage::toHtml` on the internal page * @@ -163,10 +162,14 @@ QString dataUrl(const QUrl &url) const; /** - * @brief The job requesting the URL + * @brief The `QWebEngineProfile` used by the page which parses the HTML code * + * @internal + * The default profile can't be used because of possible interferences (for example, it can cause problems if + * this class is used by an URL scheme handler whose output is HTML containing its own URL scheme; this is the + * case of the `help` scheme, for example) */ - QPointer m_request; + QWebEngineProfile *m_profile; /** * @brief The `QWebEnginePage` used to parse the HTML code produced by kioslaves @@ -176,4 +179,4 @@ }; -#endif // WEBENGINEPARTHTMLMIMETYPEHANDLER_H +#endif // WEBENGINEPARTHTMLEMBEDDER_H diff --git a/webenginepart/src/webengineparthtmlmimetypehandler.cpp b/webenginepart/src/webengineparthtmlembedder.cpp rename from webenginepart/src/webengineparthtmlmimetypehandler.cpp rename to webenginepart/src/webengineparthtmlembedder.cpp --- a/webenginepart/src/webengineparthtmlmimetypehandler.cpp +++ b/webenginepart/src/webengineparthtmlembedder.cpp @@ -22,16 +22,18 @@ * */ -#include "webengineparthtmlmimetypehandler.h" +#include "webengineparthtmlembedder.h" #include #include #include #include #include +#include #include #include +#include static const char s_extractUrlsJs[] = "extractUrlsForTag = function(name, attr){\n" " var elems = document.getElementsByTagName(name);\n" @@ -57,31 +59,31 @@ "replaceUrlsForTag(\"link\", \"href\");\n" "replaceUrlsForTag(\"map\", \"src\");"; -void WebEnginePartHtmlMimetypeHandler::requestStarted(QWebEngineUrlRequestJob* req) +WebEnginePartHtmlEmbedder::WebEnginePartHtmlEmbedder(QObject* parent) : + QObject(parent), + m_profile(new QWebEngineProfile(this)), + m_page(new QWebEnginePage(m_profile, this)) { - m_request = QPointer(req); - KIO::StoredTransferJob *job = KIO::storedGet(req->requestUrl(), KIO::NoReload, KIO::HideProgressInfo); - connect(job, &KIO::StoredTransferJob::result, this, [this, job](){m_page->setHtml(job->data(), QUrl::fromLocalFile("/"));}); + connect(this, &WebEnginePartHtmlEmbedder::urlsExtracted, this, &WebEnginePartHtmlEmbedder::startReplacingUrls); + connect(this, &WebEnginePartHtmlEmbedder::urlsReplaced, this, &WebEnginePartHtmlEmbedder::startRetrievingHtml); } -WebEnginePartHtmlMimetypeHandler::WebEnginePartHtmlMimetypeHandler(QObject* parent) : - QWebEngineUrlSchemeHandler(parent), - m_request(Q_NULLPTR), - m_page(new QWebEnginePage(this)) +void WebEnginePartHtmlEmbedder::startEmbedding(const QByteArray& html, const QString& mimeType) { - connect(m_page, &QWebEnginePage::loadFinished, this, &WebEnginePartHtmlMimetypeHandler::startExtractingUrls); - connect(this, &WebEnginePartHtmlMimetypeHandler::urlsExtracted, this, &WebEnginePartHtmlMimetypeHandler::startReplacingUrls); - connect(this, &WebEnginePartHtmlMimetypeHandler::urlsReplaced, this, &WebEnginePartHtmlMimetypeHandler::startRetrievingHtml); - connect(this, &WebEnginePartHtmlMimetypeHandler::htmlRetrieved, this, &WebEnginePartHtmlMimetypeHandler::sendReply); + //Try avoiding problems with redirection (see documentation for this class) + connect(m_page, &QWebEnginePage::loadFinished, this, &WebEnginePartHtmlEmbedder::startExtractingUrls); + m_page->setContent(html, mimeType, QUrl::fromLocalFile("/")); } -void WebEnginePartHtmlMimetypeHandler::startExtractingUrls() +void WebEnginePartHtmlEmbedder::startExtractingUrls() { + //Try avoiding problems with redirection (see documentation for this class) + disconnect(m_page, &QWebEnginePage::loadFinished, this, &WebEnginePartHtmlEmbedder::startExtractingUrls); auto lambda = [this](const QVariant &res){emit urlsExtracted(res.toStringList());}; m_page->runJavaScript(s_extractUrlsJs, lambda); } -void WebEnginePartHtmlMimetypeHandler::startReplacingUrls(const QStringList& urls) +void WebEnginePartHtmlEmbedder::startReplacingUrls(const QStringList& urls) { QStringList uniqueUrls(urls); uniqueUrls.removeDuplicates(); @@ -98,24 +100,13 @@ m_page->runJavaScript(js, [this](const QVariant &){emit urlsReplaced();}); } -void WebEnginePartHtmlMimetypeHandler::startRetrievingHtml() +void WebEnginePartHtmlEmbedder::startRetrievingHtml() { - m_page->toHtml([this](const QString &html){emit htmlRetrieved(html);}); + auto callback = [this](const QString &html){emit finished(html);}; + m_page->toHtml(callback); } -void WebEnginePartHtmlMimetypeHandler::sendReply(const QString& html) -{ - QBuffer *buf = new QBuffer(this); - connect(buf, &QIODevice::aboutToClose, buf, &QObject::deleteLater); - buf->open(QBuffer::ReadWrite); - buf->write(html.toUtf8()); - buf->seek(0); - if (m_request) { - m_request->reply("text/html", buf); - } -} - -QString WebEnginePartHtmlMimetypeHandler::dataUrl(const QUrl& url) const +QString WebEnginePartHtmlEmbedder::dataUrl(const QUrl& url) const { if (url.scheme() != "file") { return QString(); @@ -131,5 +122,3 @@ QByteArray content = file.readAll().toBase64(); return "data:" + QMimeDatabase().mimeTypeForFile(path).name()+";charset=UTF-8;base64," + content; } - - diff --git a/webenginepart/src/webenginepartkiohandler.h b/webenginepart/src/webenginepartkiohandler.h new file mode 100644 --- /dev/null +++ b/webenginepart/src/webenginepartkiohandler.h @@ -0,0 +1,317 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2018 Stefano Crocco + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 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) Q_DECL_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 messsage 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; + + /** + * @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/webenginepartkiohandler.cpp b/webenginepart/src/webenginepartkiohandler.cpp new file mode 100644 --- /dev/null +++ b/webenginepart/src/webenginepartkiohandler.cpp @@ -0,0 +1,113 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2018 Stefano Crocco + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 "webenginepartkiohandler.h" +#include "webengineparthtmlembedder.h" + +#include +#include +#include +#include + +#include + +void WebEnginePartKIOHandler::requestStarted(QWebEngineUrlRequestJob *req) +{ + m_queuedRequests << RequestJobPointer(req); + processNextRequest(); +} + +WebEnginePartKIOHandler::WebEnginePartKIOHandler(QObject* parent): + QWebEngineUrlSchemeHandler(parent), m_embedder(Q_NULLPTR) +{ + 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() +{ + if (m_mimeType.inherits("text/html") || m_mimeType.inherits("application/xhtml+xml")) { + htmlEmbedder()->startEmbedding(m_data, m_mimeType.name()); + } else { + emit ready(); + } +} + +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(); +} + +WebEnginePartHtmlEmbedder * WebEnginePartKIOHandler::htmlEmbedder() +{ + if (!m_embedder) { + m_embedder = new WebEnginePartHtmlEmbedder(this); + connect(htmlEmbedder(), &WebEnginePartHtmlEmbedder::finished, this, &WebEnginePartKIOHandler::embedderFinished); + } + return m_embedder; +}