Skip to content

Commit

Permalink
qt: force mime type resolution by filename extension only
Browse files Browse the repository at this point in the history
Fixes BitBoxSwiss#2684 and hopefully BitBoxSwiss#3061.

Without this, on linux the local mimetype database (influenced by
various packages installed on the system) can mess with the mimetype
resolution of our .html/.js/.css files served locally, resulting in a
blank page.

This manually intercepts local requests and forces mimetype resolution
by filename extension only, to avoid the system mimetype database from
breaking the app.
  • Loading branch information
benma committed Nov 21, 2024
1 parent 67352e1 commit c28da40
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

# 4.46.1
- Fix Android app crash on old Android versions
- Fix Linux blank screen issue related to the local mimetype database

# 4.46.0
- Android: enable export logs feature
Expand Down
44 changes: 39 additions & 5 deletions frontends/qt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
#include <QWebEnginePage>
#include <QWebChannel>
#include <QWebEngineUrlRequestInterceptor>
#include <QWebEngineUrlSchemeHandler>
#include <QWebEngineUrlRequestJob>
#include <QMimeDatabase>
#include <QFile>
#include <QContextMenuEvent>
#include <QMenu>
#include <QRegularExpression>
Expand Down Expand Up @@ -52,6 +56,9 @@

#define APPNAME "BitBoxApp"

// Custom scheme so we can intercept and serve local files.
static const char* scheme = "bitboxapp";

static QWebEngineView* view;
static QWebEnginePage* mainPage;
static QWebEnginePage* externalPage;
Expand Down Expand Up @@ -111,12 +118,37 @@ class WebEnginePage : public QWebEnginePage {
}
};

// Custom scheme handler so we can force mime type detection by filename extension only.
// Without this, on linux the local mimetype database can mess with the mimetype resolution.
// Programs like monodevelop or Steam/Proton install weird mime types making for example our
// .js or .html files be served with the wrogn mimetype, resulting in the webengine displaying a blank page only.
class SchemeHandler : public QWebEngineUrlSchemeHandler {
public:
void requestStarted(QWebEngineUrlRequestJob *request) override {
QUrl url = request->requestUrl();
QString path = url.path();

QMimeDatabase mimeDb;
// Force matching by extension only, not using any mimetype database.
QMimeType mimeType = mimeDb.mimeTypeForFile(path, QMimeDatabase::MatchExtension);
// Read resource from QRC (local static assets).
auto file = new QFile(":" + path);
if (file->open(QIODevice::ReadOnly)) {
request->reply(mimeType.name().toUtf8(), file);
file->setParent(request);
} else {
delete file;
request->fail(QWebEngineUrlRequestJob::UrlNotFound);
}
}
};

class RequestInterceptor : public QWebEngineUrlRequestInterceptor {
public:
explicit RequestInterceptor() : QWebEngineUrlRequestInterceptor() { }
void interceptRequest(QWebEngineUrlRequestInfo& info) override {
// Do not block qrc:/ local pages or js blobs
if (info.requestUrl().scheme() == "qrc" || info.requestUrl().scheme() == "blob") {
if (info.requestUrl().scheme() == scheme || info.requestUrl().scheme() == "blob") {
return;
}

Expand All @@ -126,8 +158,8 @@ class RequestInterceptor : public QWebEngineUrlRequestInterceptor {
// We treat the exchange pages specially because we need to allow exchange
// widgets to load in an iframe as well as let them open external links
// in a browser.
bool onExchangePage = currentUrl.contains(QRegularExpression("^qrc:/exchange/.*$"));
bool onBitsurancePage = currentUrl.contains(QRegularExpression("^qrc:/bitsurance/.*$"));
bool onExchangePage = currentUrl.contains(QRegularExpression(QString("^%1:/exchange/.*$").arg(scheme)));
bool onBitsurancePage = currentUrl.contains(QRegularExpression(QString("^%1:/bitsurance/.*$").arg(scheme)));
if (onExchangePage || onBitsurancePage) {
if (info.firstPartyUrl().toString() == info.requestUrl().toString()) {
// Ignore requests for certain file types (e.g., .js, .css) Somehow Moonpay loads
Expand All @@ -150,7 +182,7 @@ class RequestInterceptor : public QWebEngineUrlRequestInterceptor {

// All the requests originated in the wallet-connect section are allowed, as they are needed to
// load the Dapp logos and it is not easy to filter out non-images requests.
bool onWCPage = currentUrl.contains(QRegularExpression(R"(^qrc:/account/[^\/]+/wallet-connect/.*$)"));
bool onWCPage = currentUrl.contains(QRegularExpression(QString(R"(^%1:/account/[^\/]+/wallet-connect/.*$)").arg(scheme)));
if (onWCPage) {
return;
}
Expand Down Expand Up @@ -369,6 +401,8 @@ int main(int argc, char *argv[])

RequestInterceptor interceptor;
view->page()->profile()->setUrlRequestInterceptor(&interceptor);
SchemeHandler schemeHandler;
view->page()->profile()->installUrlSchemeHandler(scheme, &schemeHandler);
QObject::connect(
view->page(),
&QWebEnginePage::featurePermissionRequested,
Expand All @@ -385,7 +419,7 @@ int main(int argc, char *argv[])
channel.registerObject("backend", webClass);
view->page()->setWebChannel(&channel);
view->show();
view->load(QUrl("qrc:/index.html"));
view->load(QUrl(QString("%1:/index.html").arg(scheme)));

// Create TrayIcon
{
Expand Down

0 comments on commit c28da40

Please sign in to comment.