File indexing completed on 2024-12-22 04:43:20

0001 /*
0002     This file is part of the KDE project.
0003 
0004     SPDX-FileCopyrightText: 2018 Stefano Crocco <stefano.crocco@alice.it>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-or-later
0007 */
0008 
0009 #ifndef WEBENGINE_KIOHANDLER_H
0010 #define WEBENGINE_KIOHANDLER_H
0011 
0012 #include <QWebEngineUrlSchemeHandler>
0013 #include <QWebEngineUrlRequestJob>
0014 #include <QPointer>
0015 #include <QMimeType>
0016 
0017 namespace KIO {
0018   class StoredTransferJob;  
0019 };
0020 
0021 namespace WebEngine {
0022 
0023 class WebEnginePartHtmlEmbedder;
0024 
0025 /**
0026  * @brief Class which allows QWebEngine to access URLs provided by KIO
0027  * 
0028  * The class assumes that the data can be obtained from KIO using `KIO::storedGet` and
0029  * passes it to `QWebEngineUrlRequestJob::reply`. The mime type is determined using 
0030  * `QMimeDatabase::mimeTypeForData`. If the data is HTML or XHTML, on Qt versions before 5.12
0031  * WebEnginePartHtmlEmbedder is used to embed the contents of local URLs inside the code,
0032  * so that it can be displayed by QWebEngine without breaking cross-origin rules.
0033  * 
0034  * This class provides a virtual function, processSlaveOutput() which allows to further process KIO output before
0035  * using it as reply. Derived classes can use it to change the data, its mimetype and to set 
0036  * an error code and error message.
0037  * 
0038  * @internal
0039  * 
0040  * Since requestStarted() can be called multiple times, requests are stored in a list and 
0041  * processed one by one. This is hidden to derived classes, which only work with the current request.
0042  */
0043 class KIOHandler : public QWebEngineUrlSchemeHandler
0044 {
0045     Q_OBJECT
0046 
0047 public:
0048     /**
0049      * Constructor
0050      *
0051      * @param parent the parent object
0052      */
0053     KIOHandler(QObject* parent);
0054     
0055     /**
0056      * @brief Override of `QWebEngineUrlSchemeHandler::requestStarted`
0057      * 
0058      * It adds the new request to the list and start processing it if there aren't
0059      * other queued requests
0060      *
0061      * @param  req: the request job
0062      */
0063     void requestStarted(QWebEngineUrlRequestJob* req) override;
0064     
0065 protected slots:
0066     
0067     /**
0068     * @brief Slot called in response to the WebEnginePartHtmlEmbedder::finished() signal
0069     * 
0070     * The base class implementation calls setData() with the resulting HTML code, then
0071     * emits the ready() signal. You can override this function to do something else with
0072     * the HTML code. In this case, most likely you won't want to call the base class version
0073     * but just call setData() and emit the ready() when you're done processing the HTML code
0074     * 
0075     * @param html: the HTML code produced by the embedder 
0076     */
0077     virtual void embedderFinished(const QString &html);
0078     
0079 protected:
0080     
0081     /**
0082     * @brief Creates and returns the html embedder object
0083     * 
0084     * This is the object used to replace local URLs with their content as `data` URLs. 
0085     *
0086     * The embedder is only created on the first use of this function. When this happens, its
0087     * WebEnginePartHtmlEmbedder::finished() signal is connected to the embedderFinished(). If you
0088     * don't want this, either disconnect the signal or reimplement embedderFinished() so that it
0089     * does nothing, then connect WebEnginePartHtmlEmbedder::finished() with the appropriate slot
0090     * yourself
0091     * 
0092     * @return the html embedder
0093     */
0094     
0095     /**
0096      * @brief The request object
0097      * 
0098      * @return The request object
0099      */
0100     inline QPointer<QWebEngineUrlRequestJob> request() const {return m_currentRequest;}
0101     
0102     /**
0103      * @brief The error code to pass to `QWebEngineUrlRequestJob::fail`
0104      * 
0105      * In the base class implementation, this can be either `QWebEngineUrlRequestJob::NoError` or
0106      * `QWebEngineUrlRequestJob::RequestFailed`.
0107      * 
0108      * @return the error code to pass to `QWebEngineUrlRequestJob::fail`
0109      */
0110     inline QWebEngineUrlRequestJob::Error error() const {return m_error;}
0111     
0112     /**
0113      * @brief Sets the error code passed to `QWebEngineUrlRequestJob::fail`
0114      * 
0115      * Changes the error code. This function can be called by derived class in their
0116      * implementation of processSlaveOutput(). It should always be set to `QWebEngineUrlRequestJob::NoError`
0117      * if `QWebEngineUrlRequestJob::reply` should be called.
0118      * 
0119      * @see isSuccessful()
0120      * 
0121      * @param error: the new error code
0122      */
0123     inline void setError(QWebEngineUrlRequestJob::Error error) {m_error = error;}
0124     
0125     /**
0126      * @brief Whether an error has occurred or not
0127      * 
0128      * @return `true` if error() is `QWebEngineUrlRequestJob::NoError` and false otherwise
0129      */
0130     inline bool isSuccessful() const {return m_error == QWebEngineUrlRequestJob::NoError;}
0131     
0132     /**
0133      * @brief The error message
0134      * 
0135      * Currently, this is only used for debugging purpose
0136      * 
0137      * @return The error message given by `KJob::errorString` (or set by derived classes using setErrorMessage())
0138      *  or an empty string if KIO produced no error.
0139      */
0140     inline QString errorMessage() const {return m_errorMessage;}
0141     
0142     /**
0143      * @brief Changes the error message
0144      * 
0145      * This function can be called by derived classes from their implementation of
0146      * processSlaveOutput().
0147      * 
0148      * @param message the new error message. If error() is not `QWebEngineUrlRequestJob::NoError`, this
0149      *  should not be empty
0150      */
0151     inline void setErrorMessage(const QString &message) {m_errorMessage = message;}
0152     
0153     /**
0154      * @brief The data to pass to `QWebEngineUrlRequestJob::reply`
0155      * 
0156      * This is the data returned by the ioslave, but can be changed in processSlaveOutput() using setData()
0157      * 
0158      * @return The data to pass to QWebEngineUrlRequestJob::reply
0159      */
0160     inline QByteArray data() const {return m_data;}
0161     
0162     /**
0163      * @brief Changes the data to pass to `QWebEngineUrlRequestJob::reply`
0164      * 
0165      * This function can be used by derived classes in their implementation of processSlaveOutput(). The base 
0166      * class implementation calls it for (X)HTML code to embed the content of local files in the code itself.
0167      * 
0168      * @param data: the new data
0169      */
0170     inline void setData(const QByteArray& data) {m_data = data;}
0171     
0172     /**
0173      * @brief The mime type of the data
0174      * 
0175      * This value (actually, it's `name`) will be passed to QWebEngineUrlRequestJob::reply
0176      * @return The mime type of the data
0177      */
0178     inline QMimeType mimeType() const {return m_mimeType;}
0179     
0180     /**
0181      * @brief Changes the mime type of the data
0182      * 
0183      * This function can be used by derived classes in their implementation of processSlaveOutput()
0184      * if the mime type according to `QMimeDatabase::mimeTypeForData` is not correct or if they change
0185      * the data so that its mimetype changes from what the ioslave returned.
0186      */
0187     inline void setMimeType(const QMimeType &mime) {m_mimeType = mime;}
0188     
0189     /**
0190      * @brief Processes the output from the ioslave and replies to the request
0191      * 
0192      * In the base class implementation, if the data is (X)HTML code (according to mimetype()), the
0193      * htmlEmbedder() is used to embed the content of local files in the code. In response to 
0194      * WebEnginePartHtmlEmbedder::finished(), the ready() signal is emitted. For other mimetypes, the
0195      * ready() signal is emitted directly from here.
0196      * 
0197      * Derived classes can reimplement this function to do their own processing, of the data produced
0198      * by the ioslave. At the beginning of this function, data(), mimetype(), and errorMessage() are 
0199      * those produced by the ioslave, while error is `QWebEngineUrlRequestJob::NoError` if the ioslave 
0200      * was successful andd `QWebEngineUrlRequestJob::RequestFailed` if there were errors. Derived classes can
0201      * change these values using setData(), setMimeType(), setError() and setErrorMessage().
0202      * 
0203      * It is important that, after having changed the values as desired, the ready() signal is emitted.
0204      */
0205     virtual void processSlaveOutput();
0206     
0207 signals:
0208     
0209     /**
0210     * @brief Signal emitted when processing of data produced by the ioslave has finished
0211     * 
0212     * In response to this signal, sendReply() is called, sending the response to the original request
0213     */
0214     void ready();
0215     
0216 private slots:
0217     
0218     /**
0219      * Slot called when the ioslave finishes
0220      * 
0221      * It reads the data, mimetype and error state from the job and stores it.
0222      * 
0223      * @param job: the finished ioslave job
0224      */
0225     
0226     void kioJobFinished(KIO::StoredTransferJob *job);
0227     
0228     /**
0229      * @brief Sends a reply to the `QWebEngineUrlRequestJob`
0230      * 
0231      * The reply is sent using the values returned by data(), mimeType(), error() and errorMessage().
0232      * 
0233      * If error() is something else from `QWebEngineUrlRequestJob::NoError`, this function calls
0234      * `QWebEngineUrlRequestJob::fail` with the corresponding error code, otherwise it calls 
0235      * `QWebEngineUrlRequestJob::reply()` passing data() and mimetype() as arguments.
0236      * 
0237      * This slot is called in response to the ready() signal
0238      */
0239     void sendReply();
0240     
0241 private:
0242     
0243     /**
0244      * @brief Starts processing the next `QWebEngineUrlRequestJob` in queue
0245      * 
0246      * @internal
0247      * This function removes the first (valid) request from #m_queuedRequests,
0248      * stores it in #m_currentRequest and starts a `KIO::storedGet` for its URL
0249      */
0250     void processNextRequest();
0251 
0252     /**
0253      * @brief Creates a reply from the error string of the given job
0254      *
0255      * The reply is stored in m_data
0256      *
0257      * This function does nothing if the job didn't report an error or if the error string is empty
0258      * @param job the job
0259      */
0260     void createDataFromErrorString(KIO::StoredTransferJob *job);
0261     
0262     using RequestJobPointer = QPointer<QWebEngineUrlRequestJob>;
0263     
0264     /**
0265      * @brief A list of requests to be processed
0266      */
0267     QList<RequestJobPointer> m_queuedRequests;
0268     
0269     /**
0270      * @brief The request currently being processed
0271      */
0272     RequestJobPointer m_currentRequest;
0273     
0274     /**
0275      * @brief The error code to use for `QWebEngineUrlRequestJob::fail` for the current request
0276      * 
0277      * This is valid only after the call to kioJobFinished()
0278      */
0279     QWebEngineUrlRequestJob::Error m_error;
0280     
0281     /**
0282      * @brief The error message produced by KIO for the current request
0283      * 
0284      * Currently, this is only used for debugging purposes
0285      * 
0286      * This is valid only after the call to kioJobFinished()
0287      */
0288     QString m_errorMessage;
0289     
0290     /**
0291      * @brief The data produced by KIO for the current request
0292      * 
0293      * This is valid only after the call to kioJobFinished()
0294      */ 
0295     QByteArray m_data;
0296     
0297     /**
0298      * @brief The mime type of the data produced by KIO for the current request
0299      * 
0300      * This is valid only after the call to kioJobFinished()
0301      */ 
0302     QMimeType m_mimeType;
0303     
0304 };
0305 }
0306 
0307 #endif // WEBENGINE_KIOHANDLER_H