Support Qt 6.4 WebEngine permissions
ober
c303afc3d3b0b759044600d30cfed0c98389c005
--- a/qt-webengine/src/browser_view.cpp +++ b/qt-webengine/src/browser_view.cpp @@ -13,7 +13,14 @@ #include <QEvent> #include <QUrl> #include <QWebEngineCertificateError> +#include <QtGlobal> + +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +#define JWB_HAS_QWEBENGINE_PERMISSION 1 #include <QWebEnginePermission> +#else +#define JWB_HAS_QWEBENGINE_PERMISSION 0 +#endif #include <set> #include <cstdio> @@ -71,6 +78,12 @@ std::string g_tls_last_error; std::set<QString> g_permission_allow_once; std::string g_permission_last_request; +#if JWB_HAS_QWEBENGINE_PERMISSION +using BrowserPermissionType = QWebEnginePermission::PermissionType; +#else +using BrowserPermissionType = QWebEnginePage::Feature; +#endif + QString normalized_host(const QUrl &url) { return url.host().toLower(); } std::string tls_error_record(const QWebEngineCertificateError &error) { @@ -104,8 +117,8 @@ void handle_certificate_error(const QWebEngineCertificateError &error) { decision.rejectCertificate(); } -const char *permission_type_name(QWebEnginePermission::PermissionType type) { - using Type = QWebEnginePermission::PermissionType; +const char *permission_type_name(BrowserPermissionType type) { + using Type = BrowserPermissionType; switch (type) { case Type::MediaAudioCapture: return "media-audio"; @@ -123,19 +136,22 @@ const char *permission_type_name(QWebEnginePermission::PermissionType type) { return "notifications"; case Type::Geolocation: return "geolocation"; +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) case Type::ClipboardReadWrite: return "clipboard-read-write"; case Type::LocalFontsAccess: return "local-fonts"; +#endif +#if JWB_HAS_QWEBENGINE_PERMISSION case Type::Unsupported: +#endif default: return "unsupported"; } } -bool permission_type_from_name(const QString &name, - QWebEnginePermission::PermissionType *out) { - using Type = QWebEnginePermission::PermissionType; +bool permission_type_from_name(const QString &name, BrowserPermissionType *out) { + using Type = BrowserPermissionType; if (name == QLatin1String("media-audio")) *out = Type::MediaAudioCapture; else if (name == QLatin1String("media-video")) *out = Type::MediaVideoCapture; else if (name == QLatin1String("media-audio-video")) *out = Type::MediaAudioVideoCapture; @@ -144,31 +160,35 @@ bool permission_type_from_name(const QString &name, else if (name == QLatin1String("mouse-lock")) *out = Type::MouseLock; else if (name == QLatin1String("notifications")) *out = Type::Notifications; else if (name == QLatin1String("geolocation")) *out = Type::Geolocation; +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) else if (name == QLatin1String("clipboard-read-write")) *out = Type::ClipboardReadWrite; else if (name == QLatin1String("local-fonts")) *out = Type::LocalFontsAccess; +#endif else return false; return true; } -bool permission_allowed_by_caps(uint32_t caps, - QWebEnginePermission::PermissionType type) { - using Type = QWebEnginePermission::PermissionType; +bool permission_allowed_by_caps(uint32_t caps, BrowserPermissionType type) { + using Type = BrowserPermissionType; switch (type) { case Type::MediaAudioCapture: case Type::MediaVideoCapture: case Type::MediaAudioVideoCapture: return (caps & JWB_CAP_MEDIA) != 0; +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) case Type::ClipboardReadWrite: return (caps & JWB_CAP_CLIPBOARD) != 0; +#endif default: return false; } } -QString permission_key(const QString &origin, QWebEnginePermission::PermissionType type) { +QString permission_key(const QString &origin, BrowserPermissionType type) { return origin + QLatin1Char('\t') + QString::fromLatin1(permission_type_name(type)); } +#if JWB_HAS_QWEBENGINE_PERMISSION void handle_permission_request(uint32_t caps, const QWebEnginePermission &request) { if (!request.isValid()) return; const QString origin = request.origin().toString(); @@ -186,6 +206,27 @@ void handle_permission_request(uint32_t caps, const QWebEnginePermission &reques permission_type_name(type), origin.toUtf8().constData()); request.deny(); } +#else +void handle_permission_request(QWebEnginePage *page, uint32_t caps, + const QUrl &origin_url, + BrowserPermissionType type) { + const QString origin = origin_url.toString(); + const QString key = permission_key(origin, type); + const auto grant = g_permission_allow_once.find(key); + if (grant != g_permission_allow_once.end() && permission_allowed_by_caps(caps, type)) { + g_permission_allow_once.erase(grant); + g_permission_last_request.clear(); + page->setFeaturePermission(origin_url, type, + QWebEnginePage::PermissionGrantedByUser); + return; + } + g_permission_last_request = key.toStdString(); + std::fprintf(stderr, "[permission] denied %s for %s\n", + permission_type_name(type), origin.toUtf8().constData()); + page->setFeaturePermission(origin_url, type, + QWebEnginePage::PermissionDeniedByUser); +} +#endif } // namespace @@ -260,15 +301,24 @@ void *backend_view_create(void *context_obj, JwbStatus *err) { public: PolicyPage(Context *context, QObject *parent) : QWebEnginePage(context->profile, parent), context_(context) { - QObject::connect(this, &QWebEnginePage::certificateError, this, - [](const QWebEngineCertificateError &error) { - handle_certificate_error(error); - }); - QObject::connect(this, &QWebEnginePage::permissionRequested, this, - [this](QWebEnginePermission request) { - handle_permission_request(context_->caps, request); - }); - } + QObject::connect(this, &QWebEnginePage::certificateError, this, + [](const QWebEngineCertificateError &error) { + handle_certificate_error(error); + }); +#if JWB_HAS_QWEBENGINE_PERMISSION + QObject::connect(this, &QWebEnginePage::permissionRequested, this, + [this](QWebEnginePermission request) { + handle_permission_request(context_->caps, request); + }); +#else + QObject::connect(this, &QWebEnginePage::featurePermissionRequested, this, + [this](const QUrl &origin, + QWebEnginePage::Feature feature) { + handle_permission_request(this, context_->caps, + origin, feature); + }); +#endif + } protected: bool acceptNavigationRequest(const QUrl &url, NavigationType type, @@ -374,7 +424,7 @@ JWB_API int jwb_permission_allow_once(const char *origin, const char *type) { } const QString o = QString::fromUtf8(origin); const QString t = QString::fromUtf8(type); - QWebEnginePermission::PermissionType parsed; + BrowserPermissionType parsed; if (o.contains(QLatin1Char('\t')) || t.contains(QLatin1Char('\t')) || !permission_type_from_name(t, &parsed)) { jwb::set_last_error("permission_allow_once: invalid origin or type"); @@ -389,7 +439,7 @@ JWB_API int jwb_permission_clear(const char *origin, const char *type) { if (!origin || !*origin || !type || !*type) return 1; const QString o = QString::fromUtf8(origin); const QString t = QString::fromUtf8(type); - QWebEnginePermission::PermissionType parsed; + BrowserPermissionType parsed; if (!permission_type_from_name(t, &parsed)) return 1; g_permission_allow_once.erase(permission_key(o, parsed)); return 1;