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