From af7e365b1516d660d271475fdfe0c20ae09e66a8 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sat, 27 Mar 2021 18:52:22 +0200 Subject: qt: Make PACKAGE_BUGREPORT link clickable Co-authored-by: Russell Yanofsky --- src/qt/bitcoin.cpp | 7 ++++++- src/qt/guiutil.cpp | 9 +++++++++ src/qt/guiutil.h | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src/qt') diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index fc6d0febc2..aec63ffaf0 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -45,10 +45,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -417,7 +419,10 @@ void BitcoinApplication::shutdownResult() void BitcoinApplication::handleRunawayException(const QString &message) { - QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + QString("

") + message); + QMessageBox::critical( + nullptr, tr("Runaway exception"), + tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) % + QLatin1String("

") % GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT)); ::exit(EXIT_FAILURE); } diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index b4afdbcc22..36fcc4d361 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #include // for Qt::mightBeRichText #include #include @@ -893,4 +895,11 @@ QImage GetImage(const QLabel* label) #endif } +QString MakeHtmlLink(const QString& source, const QString& link) +{ + return QString(source).replace( + link, + QLatin1String("") % link % QLatin1String("")); +} + } // namespace GUIUtil diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 6395ec6abd..6ab0a71a96 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -327,6 +327,11 @@ namespace GUIUtil QObject::connect(&source, &QObject::destroyed, object, std::forward(function), connection); } + /** + * Replaces a plain text link with an HTML tagged one. + */ + QString MakeHtmlLink(const QString& source, const QString& link); + } // namespace GUIUtil #endif // BITCOIN_QT_GUIUTIL_H -- cgit v1.2.3 From 64a8755af396f1c2791018510e22b58114e68594 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sat, 27 Mar 2021 18:52:22 +0200 Subject: qt: Add BitcoinApplication::handleNonFatalException function This helper function will be used in the following commits. Co-authored-by: Russell Yanofsky --- src/qt/bitcoin.cpp | 10 ++++++++++ src/qt/bitcoin.h | 6 ++++++ 2 files changed, 16 insertions(+) (limited to 'src/qt') diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index aec63ffaf0..de71b7dea7 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -426,6 +426,16 @@ void BitcoinApplication::handleRunawayException(const QString &message) ::exit(EXIT_FAILURE); } +void BitcoinApplication::handleNonFatalException(const QString& message) +{ + assert(QThread::currentThread() == thread()); + QMessageBox::warning( + nullptr, tr("Internal error"), + tr("An internal error occurred. %1 will attempt to continue safely. This is " + "an unexpected bug which can be reported as described below.").arg(PACKAGE_NAME) % + QLatin1String("

") % GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT)); +} + WId BitcoinApplication::getMainWinId() const { if (!window) diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h index 69e0a5921e..5fd6bd607f 100644 --- a/src/qt/bitcoin.h +++ b/src/qt/bitcoin.h @@ -99,6 +99,12 @@ public Q_SLOTS: /// Handle runaway exceptions. Shows a message box with the problem and quits the program. void handleRunawayException(const QString &message); + /** + * A helper function that shows a message box + * with details about a non-fatal exception. + */ + void handleNonFatalException(const QString& message); + Q_SIGNALS: void requestedInitialize(); void requestedShutdown(); -- cgit v1.2.3 From f7e260a471010e2d656fbc5ea8c310f6d94c26b9 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sat, 27 Mar 2021 18:52:22 +0200 Subject: qt: Add GUIUtil::ExceptionSafeConnect function Throwing an exception from a slot invoked by Qt's signal-slot connection mechanism is considered undefined behavior, unless it is handled within the slot. The GUIUtil::ExceptionSafeConnect function should be used for exception handling within slots. --- src/qt/guiutil.cpp | 11 +++++++++++ src/qt/guiutil.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) (limited to 'src/qt') diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 36fcc4d361..0e91f9f385 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -902,4 +902,15 @@ QString MakeHtmlLink(const QString& source, const QString& link) QLatin1String("") % link % QLatin1String("")); } +void PrintSlotException( + const std::exception* exception, + const QObject* sender, + const QObject* receiver) +{ + std::string description = sender->metaObject()->className(); + description += "->"; + description += receiver->metaObject()->className(); + PrintExceptionContinue(exception, description.c_str()); +} + } // namespace GUIUtil diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 6ab0a71a96..a1cf274354 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -9,18 +9,23 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include #include +#include #include +#include class QValidatedLineEdit; class SendCoinsRecipient; @@ -332,6 +337,53 @@ namespace GUIUtil */ QString MakeHtmlLink(const QString& source, const QString& link); + void PrintSlotException( + const std::exception* exception, + const QObject* sender, + const QObject* receiver); + + /** + * A drop-in replacement of QObject::connect function + * (see: https://doc.qt.io/qt-5/qobject.html#connect-3), that + * guaranties that all exceptions are handled within the slot. + * + * NOTE: This function is incompatible with Qt private signals. + */ + template + auto ExceptionSafeConnect( + Sender sender, Signal signal, Receiver receiver, Slot method, + Qt::ConnectionType type = Qt::AutoConnection) + { + return QObject::connect( + sender, signal, receiver, + [sender, receiver, method](auto&&... args) { + bool ok{true}; + try { + (receiver->*method)(std::forward(args)...); + } catch (const NonFatalCheckError& e) { + PrintSlotException(&e, sender, receiver); + ok = QMetaObject::invokeMethod( + qApp, "handleNonFatalException", + blockingGUIThreadConnection(), + Q_ARG(QString, QString::fromStdString(e.what()))); + } catch (const std::exception& e) { + PrintSlotException(&e, sender, receiver); + ok = QMetaObject::invokeMethod( + qApp, "handleRunawayException", + blockingGUIThreadConnection(), + Q_ARG(QString, QString::fromStdString(e.what()))); + } catch (...) { + PrintSlotException(nullptr, sender, receiver); + ok = QMetaObject::invokeMethod( + qApp, "handleRunawayException", + blockingGUIThreadConnection(), + Q_ARG(QString, "Unknown failure occurred.")); + } + assert(ok); + }, + type); + } + } // namespace GUIUtil #endif // BITCOIN_QT_GUIUTIL_H -- cgit v1.2.3 From eb6156ba1b4c303eb597e3fc4a9e42ce45e6e78d Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sat, 27 Mar 2021 19:10:03 +0200 Subject: qt: Handle exceptions in BitcoinGUI::addWallet slot --- src/qt/bitcoingui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/qt') diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6677c9e3b5..3abca7dcff 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -654,7 +654,7 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller) m_open_wallet_action->setEnabled(true); m_open_wallet_action->setMenu(m_open_wallet_menu); - connect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet); + GUIUtil::ExceptionSafeConnect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet); connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet); for (WalletModel* wallet_model : m_wallet_controller->getOpenWallets()) { -- cgit v1.2.3 From bc00e13bc800863641b3e1e64732a38418d3022f Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sat, 27 Mar 2021 19:31:37 +0200 Subject: qt: Handle exceptions in WalletModel::pollBalanceChanged slot Actually, the private QTimer::timeout signal has one QTimer::QPrivateSignal parameter. --- src/qt/walletmodel.cpp | 5 ++++- src/qt/walletmodel.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src/qt') diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 02254da3ce..c6dd0c2ad6 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -65,7 +65,10 @@ WalletModel::~WalletModel() void WalletModel::startPollBalance() { // This timer will be fired repeatedly to update the balance - connect(timer, &QTimer::timeout, this, &WalletModel::pollBalanceChanged); + // Since the QTimer::timeout is a private signal, it cannot be used + // in the GUIUtil::ExceptionSafeConnect directly. + connect(timer, &QTimer::timeout, this, &WalletModel::timerTimeout); + GUIUtil::ExceptionSafeConnect(this, &WalletModel::timerTimeout, this, &WalletModel::pollBalanceChanged); timer->start(MODEL_UPDATE_DELAY); } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 9a3c3f2f66..4ca8643444 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -223,6 +223,8 @@ Q_SIGNALS: // Notify that there are now keys in the keypool void canGetAddressesChanged(); + void timerTimeout(); + public Q_SLOTS: /* Starts a timer to periodically update the balance */ void startPollBalance(); -- cgit v1.2.3 From 1ac2bc7ac070dfd1df1872d759540b0c92495301 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sat, 27 Mar 2021 19:33:26 +0200 Subject: qt: Handle exceptions in TransactionView::bumpFee slot Also the parameter list of the TransactionView::bumpFee slot is made compatible with one of the QAction::triggered signal. --- src/qt/transactionview.cpp | 4 ++-- src/qt/transactionview.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/qt') diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 42e08c6af7..1511410129 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -199,7 +199,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa connect(transactionView, &QTableView::doubleClicked, this, &TransactionView::doubleClicked); connect(transactionView, &QTableView::customContextMenuRequested, this, &TransactionView::contextualMenu); - connect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee); + GUIUtil::ExceptionSafeConnect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee); connect(abandonAction, &QAction::triggered, this, &TransactionView::abandonTx); connect(copyAddressAction, &QAction::triggered, this, &TransactionView::copyAddress); connect(copyLabelAction, &QAction::triggered, this, &TransactionView::copyLabel); @@ -424,7 +424,7 @@ void TransactionView::abandonTx() model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false); } -void TransactionView::bumpFee() +void TransactionView::bumpFee([[maybe_unused]] bool checked) { if(!transactionView || !transactionView->selectionModel()) return; diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 35ada4aa7a..66350bdc02 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -99,7 +99,7 @@ private Q_SLOTS: void openThirdPartyTxUrl(QString url); void updateWatchOnlyColumn(bool fHaveWatchOnly); void abandonTx(); - void bumpFee(); + void bumpFee(bool checked); Q_SIGNALS: void doubleClicked(const QModelIndex&); -- cgit v1.2.3 From b8e5d0d3fe3386807d47f50d13ac34fcd2a538fd Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sat, 27 Mar 2021 19:57:39 +0200 Subject: qt: Handle exceptions in SendCoinsDialog::sendButtonClicked slot Also, uic automatic connection replaced with an explicit one. --- src/qt/sendcoinsdialog.cpp | 4 +++- src/qt/sendcoinsdialog.h | 2 +- src/qt/test/wallettests.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src/qt') diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 95e1ce2210..f0e720617c 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -129,6 +129,8 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p ui->customFee->SetAllowEmpty(false); ui->customFee->setValue(settings.value("nTransactionFee").toLongLong()); minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool()); + + GUIUtil::ExceptionSafeConnect(ui->sendButton, &QPushButton::clicked, this, &SendCoinsDialog::sendButtonClicked); } void SendCoinsDialog::setClientModel(ClientModel *_clientModel) @@ -375,7 +377,7 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa return true; } -void SendCoinsDialog::on_sendButton_clicked() +void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked) { if(!model || !model->getOptionsModel()) return; diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 4fc2f57cd6..3e276201ba 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -80,7 +80,7 @@ private: void updateCoinControlState(CCoinControl& ctrl); private Q_SLOTS: - void on_sendButton_clicked(); + void sendButtonClicked(bool checked); void on_buttonChooseFee_clicked(); void on_buttonMinimizeFee_clicked(); void removeEntry(SendCoinsEntry* entry); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 1107c44dc9..03460cd6eb 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -73,7 +73,7 @@ uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDe if (status == CT_NEW) txid = hash; })); ConfirmSend(); - bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked"); + bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "sendButtonClicked", Q_ARG(bool, false)); assert(invoked); return txid; } -- cgit v1.2.3