Support Qt 6.4 WebEngine permissions

ober

c303afc3d3b0b759044600d30cfed0c98389c005

diff --git a/qt-webengine/src/browser_view.cpp b/qt-webengine/src/browser_view.cpp
index 1846c2a..130fa11 100644
--- 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;