File indexing completed on 2024-05-19 05:01:19

0001 /*
0002     This file is part of the KDE project.
0003 
0004     SPDX-FileCopyrightText: 2021 Stefano Crocco <stefano.crocco@alice.it>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #ifndef WEBENGINEPART_CERTIFICATEERRORDIALOGMANAGER_H
0010 #define WEBENGINEPART_CERTIFICATEERRORDIALOGMANAGER_H
0011 
0012 #include <QObject>
0013 #include <QWebEngineCertificateError>
0014 #include <QPointer>
0015 
0016 class WebEnginePage;
0017 
0018 namespace KonqWebEnginePart {
0019 
0020     class WebEnginePartCertificateErrorDlg;
0021 
0022     /**
0023      * @brief Class which takes care of queuing dialogs reporting certificate errors.
0024      *
0025      * Since calls to WebEnginePage::certificateError are made asynchronously, without
0026      * queuing it could happen that a second dialog is shown when there's another one
0027      * still visible. Since the dialogs are modal and they can cover each other, this can
0028      * cause issues for the user. To avoid this, this class ensures that only dialogs from
0029      * different windows can be shown at the same time.
0030      *
0031      * @internal The whole issue is caused by the use of @c QDialog::open instead of
0032      * @c QDialog::exec to display the dialog: since @c exec is synchronous, it will prevent
0033      * displaying a second dialog before the first has been closed.
0034      */
0035     class CertificateErrorDialogManager : public QObject
0036     {
0037         Q_OBJECT
0038 
0039     public:
0040         /**
0041          * @brief Constructor
0042          *
0043          * @param parent the parent object
0044          */
0045         CertificateErrorDialogManager(QObject *parent=nullptr);
0046 
0047         /**
0048          * @brief Destructor
0049          *
0050          */
0051         ~CertificateErrorDialogManager();
0052 
0053         /**
0054          * @brief Displays a dialog regarding the certificate error or queues the certificate error
0055          *
0056          * If there's no currently displayed certificate error dialog for the window containing the page,
0057          * the dialog for the new certificate is immediately shown, otherwise the certificate error
0058          * is put in queue waiting to be displayed as soon as there aren't other dialogs for that
0059          * window.
0060          *
0061          * In all cases, the appropriate function is called on the certificate: @c ignoreCertificateError
0062          * or @c rejectCertificate if the dialog was displayed immediately or @c defer if it was queued.
0063          * @param _ce the certificate error
0064          * @param page the page which caused the certificate error
0065          * @return @b true if the certificate was overridable and @b false if it wasn't overridable.
0066          * @note In case the certificate wasn't overridable, @c rejectCertificate will be called on the
0067          * certificate.
0068          * @note In Qt6 the return value is ignored
0069          * @todo When removing compatibility with KF5, change the return value to `void`
0070          */
0071         bool handleCertificateError(const QWebEngineCertificateError  &_ce, WebEnginePage *page);
0072 
0073     private:
0074 
0075         /**
0076          * @brief Struct used to encapuslate information about certificate errors
0077          */
0078         struct CertificateErrorData {
0079             /**
0080              * @brief The certificate error
0081              */
0082             QWebEngineCertificateError error;
0083             /**
0084              * @brief The page which has caused the certificate error
0085              */
0086             QPointer<WebEnginePage> page;
0087         };
0088 
0089         /**
0090          * @brief Whether the decision to always ignore the given error is recorded in the user settings
0091          *
0092          * @param ce the certificate error
0093          * @return @b true if the user has decided to always ignore the error and @b false otherwise
0094          */
0095         static bool userAlreadyChoseToIgnoreError(const QWebEngineCertificateError  &ce);
0096 
0097         /**
0098          * @brief Records the user's choice to forever ignore the given error
0099          *
0100          * @param ce the error
0101          */
0102         static void recordIgnoreForeverChoice(const QWebEngineCertificateError &ce);
0103 
0104         /**
0105          * @brief The window (toplevel widget) associated to the given page
0106          *
0107          * @param page the page to retrieve the window for
0108          * @return the window associated with @p page or @b nullptr if either @p page or its @c view are @b nullptr
0109          */
0110         static QWidget* windowForPage(WebEnginePage *page);
0111 
0112         /**
0113          * @brief Displays a dialog for the given certificate error unless a certificate error dialog
0114          * for the same window is already visible.
0115          *
0116          * @param data the data describing the certificate error
0117          * @return @b true if the dialog was displayed and @b false if it wasn't
0118          * @note the dialog is displayed asynchronously
0119          */
0120         bool displayDialogIfPossible(const CertificateErrorData &data);
0121 
0122         /**
0123          * @brief Displays a dialog for the given certificate error
0124          *
0125          * The dialog is displayed asynchronously (using @c QDialog::open). The dialog result is used
0126          * in dialogClosed.
0127          *
0128          * @warning This function doesn't check whether a dialog for the given window
0129          * is already visible: it assumes that such check has been done by the caller.
0130          * @warning This function @b doesn't use windowForPage to determine which window the page belongs to:
0131          * it always uses @p window.
0132          *
0133          * @internal This function stores the new dialog and the corresponding window in #m_dialogs
0134          *
0135          * @param data the data describing the certificate error
0136          * @param window the window the dialog should be displayed in
0137          */
0138         void displayDialog(const CertificateErrorData &data, QWidget *window);
0139 
0140 
0141     private slots:
0142         /**
0143          * @brief Removes a dialog from the list and displays the next dialog (if any) for the same window
0144          *
0145          * @param obj the dialog to remove. It is a @c QObject rather than a WebEnginePartCertificateErrorDlg
0146          * because this slot is called in response to the dialog @c destroyed signal, which has a @c QObject* as argument
0147          */
0148         void removeDestroyedDialog(QObject *dlg);
0149 
0150         /**
0151          * @brief Removes any dialog from the given window from the list
0152          *
0153          * @param obj the window. It is a @c QObject rather than a @c QWidget
0154          * because this slot is called in response to the window @c destroyed signal, which has a @c QObject* as argument
0155          */
0156         void removeDestroyedWindow(QObject *window);
0157 
0158         /**
0159          * @brief Applies the choice made by the user in the given dialog
0160          *
0161          * This function is called in response to the dialog's @b finished signal.
0162          * It calls @c ignoreCertificateError or @c rejectCertificate on the certificate error (retrieved using
0163          * WebEnginePartCertificateErrorDlg::certificateError) and, if the user chose to always ignore the error,
0164          * records the choice.
0165          *
0166          * This function also deletes the dialog (using @c deleteLater).
0167          *
0168          * @note This function @b doesn't display the next dialog for the associated window: that task is left to
0169          * removeDestroyedDialog, which is called in response to the dialog destruction.
0170          *
0171          * @param dlg the dialog
0172          */
0173         void applyUserChoice(WebEnginePartCertificateErrorDlg *dlg);
0174 
0175         /**
0176          * @brief Displays the certificate error dialog for the next error caused by a page associated with the given window
0177          *
0178          * If no such certificate error exists, nothing is done.
0179          *
0180          * @param window the window to display the next error for.
0181          */
0182         void displayNextDialog(QWidget *window);
0183 
0184     private:
0185 
0186         /**
0187          * @brief A list of all the queued certificate errors together with the corresponding pages
0188          *
0189          * When a dialog for a certificate error is created, the corresponding entry is removed from the list
0190          * @internal The entry is removed when the dialog is created, not when it's destroyed
0191          *
0192          */
0193         QVector<CertificateErrorData> m_certificates;
0194 
0195         /**
0196          * @brief A hash associating each certificate error dialog with its window
0197          *
0198          * If the window is destroyed, the corresponding entry is removed from the list.
0199          * @note Keys and values are @c QObject* rather than more specific types because they're used
0200          * in slots connected with @c QObject::destroyed signals, whose argument is a @c QObject*.
0201          */
0202         QHash<QObject*, QObject*> m_dialogs;
0203 
0204     };
0205 
0206 }
0207 
0208 #endif // WEBENGINEPART_CERTIFICATEERRORDIALOGMANAGER_H