Title: [280172] trunk
Revision
280172
Author
[email protected]
Date
2021-07-22 03:54:07 -0700 (Thu, 22 Jul 2021)

Log Message

[GLib] Expose API to access/modify capture devices states
https://bugs.webkit.org/show_bug.cgi?id=227902

Reviewed by Carlos Garcia Campos.

Source/WebKit:

Introduce new GLib API (and corresponding GObject properties):

webkit_web_view_get_camera_capture_state
webkit_web_view_get_microphone_capture_state
webkit_web_view_set_camera_capture_state
webkit_web_view_set_microphone_capture_state
webkit_web_view_get_display_capture_state
webkit_web_view_set_display_capture_state
webkit_user_media_permission_is_for_display_device

This can be useful in Web browsers willing to indicate the status of the capture devices
currently in use by the WebView.

Covered by API tests.

* UIProcess/API/glib/WebKitUIClient.cpp:
* UIProcess/API/glib/WebKitUserMediaPermissionRequest.cpp:
(webkit_user_media_permission_is_for_display_device):
* UIProcess/API/glib/WebKitWebView.cpp:
(webkitWebViewMediaCaptureStateDidChange):
(webkitWebViewSetProperty):
(webkitWebViewGetProperty):
(webkit_web_view_class_init):
(webkitWebViewConfigureMediaCapture):
(webkit_web_view_get_camera_capture_state):
(webkit_web_view_set_camera_capture_state):
(webkit_web_view_get_microphone_capture_state):
(webkit_web_view_set_microphone_capture_state):
(webkit_web_view_get_display_capture_state):
(webkit_web_view_set_display_capture_state):
* UIProcess/API/glib/WebKitWebViewPrivate.h:
* UIProcess/API/gtk/WebKitUserMediaPermissionRequest.h:
* UIProcess/API/gtk/WebKitWebView.h:
* UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
* UIProcess/API/wpe/WebKitUserMediaPermissionRequest.h:
* UIProcess/API/wpe/WebKitWebView.h:
* UIProcess/API/wpe/docs/wpe-1.0-sections.txt:

Tools:

The GTK MiniBrowser is now able to show media capture indicators, through the URI entry. A
new API test was added, checking support for getDisplayMedia().

* MiniBrowser/gtk/BrowserTab.c:
(decidePermissionRequest):
* MiniBrowser/gtk/BrowserWindow.c:
(webViewMediaCaptureStateChanged):
(webViewUriEntryIconPressed):
(browserWindowSwitchTab):
* TestWebKitAPI/Tests/WebKitGLib/TestUIClient.cpp:
(testWebViewUserMediaPermissionRequests):
(testWebViewAudioOnlyUserMediaPermissionRequests):
(testWebViewDisplayUserMediaPermissionRequests):
(beforeAll):
* TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp:
(displayCaptureChanged):
(WebViewTest::waitUntilDisplayCaptureStateChangedTo):
(microphoneCaptureChanged):
(WebViewTest::waitUntilMicrophoneCaptureStateChangedTo):
(cameraCaptureChanged):
(WebViewTest::waitUntilCameraCaptureStateChangedTo):
* TestWebKitAPI/glib/WebKitGLib/WebViewTest.h:
* flatpak/flatpakutils.py:
(WebkitFlatpak.run_in_sandbox):

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (280171 => 280172)


--- trunk/Source/WebKit/ChangeLog	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/ChangeLog	2021-07-22 10:54:07 UTC (rev 280172)
@@ -1,3 +1,48 @@
+2021-07-22  Philippe Normand  <[email protected]>
+
+        [GLib] Expose API to access/modify capture devices states
+        https://bugs.webkit.org/show_bug.cgi?id=227902
+
+        Reviewed by Carlos Garcia Campos.
+
+        Introduce new GLib API (and corresponding GObject properties):
+
+        webkit_web_view_get_camera_capture_state
+        webkit_web_view_get_microphone_capture_state
+        webkit_web_view_set_camera_capture_state
+        webkit_web_view_set_microphone_capture_state
+        webkit_web_view_get_display_capture_state
+        webkit_web_view_set_display_capture_state
+        webkit_user_media_permission_is_for_display_device
+
+        This can be useful in Web browsers willing to indicate the status of the capture devices
+        currently in use by the WebView.
+
+        Covered by API tests.
+
+        * UIProcess/API/glib/WebKitUIClient.cpp:
+        * UIProcess/API/glib/WebKitUserMediaPermissionRequest.cpp:
+        (webkit_user_media_permission_is_for_display_device):
+        * UIProcess/API/glib/WebKitWebView.cpp:
+        (webkitWebViewMediaCaptureStateDidChange):
+        (webkitWebViewSetProperty):
+        (webkitWebViewGetProperty):
+        (webkit_web_view_class_init):
+        (webkitWebViewConfigureMediaCapture):
+        (webkit_web_view_get_camera_capture_state):
+        (webkit_web_view_set_camera_capture_state):
+        (webkit_web_view_get_microphone_capture_state):
+        (webkit_web_view_set_microphone_capture_state):
+        (webkit_web_view_get_display_capture_state):
+        (webkit_web_view_set_display_capture_state):
+        * UIProcess/API/glib/WebKitWebViewPrivate.h:
+        * UIProcess/API/gtk/WebKitUserMediaPermissionRequest.h:
+        * UIProcess/API/gtk/WebKitWebView.h:
+        * UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
+        * UIProcess/API/wpe/WebKitUserMediaPermissionRequest.h:
+        * UIProcess/API/wpe/WebKitWebView.h:
+        * UIProcess/API/wpe/docs/wpe-1.0-sections.txt:
+
 2021-07-22  Martin Robinson  <[email protected]>
 
         [css-scroll-snap] Pass the full target point when selecting a snap offset

Modified: trunk/Source/WebKit/UIProcess/API/glib/WebKitUIClient.cpp (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/glib/WebKitUIClient.cpp	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/glib/WebKitUIClient.cpp	2021-07-22 10:54:07 UTC (rev 280172)
@@ -352,6 +352,11 @@
         webkitWebViewIsPlayingAudioChanged(m_webView);
     }
 
+    void mediaCaptureStateDidChange(WebCore::MediaProducer::MediaStateFlags mediaStateFlags) final
+    {
+        webkitWebViewMediaCaptureStateDidChange(m_webView, mediaStateFlags);
+    }
+
 #if ENABLE(POINTER_LOCK)
     void requestPointerLock(WebPageProxy* page) final
     {

Modified: trunk/Source/WebKit/UIProcess/API/glib/WebKitUserMediaPermissionRequest.cpp (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/glib/WebKitUserMediaPermissionRequest.cpp	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/glib/WebKitUserMediaPermissionRequest.cpp	2021-07-22 10:54:07 UTC (rev 280172)
@@ -118,7 +118,7 @@
  */
 gboolean webkit_user_media_permission_is_for_audio_device(WebKitUserMediaPermissionRequest* request)
 {
-    g_return_val_if_fail(request->priv->request, FALSE);
+    g_return_val_if_fail(WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(request), FALSE);
     return request->priv->request->requiresAudioCapture();
 }
 
@@ -132,10 +132,24 @@
  */
 gboolean webkit_user_media_permission_is_for_video_device(WebKitUserMediaPermissionRequest* request)
 {
-    g_return_val_if_fail(request->priv->request, FALSE);
+    g_return_val_if_fail(WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(request), FALSE);
     return request->priv->request->requiresVideoCapture();
 }
 
+/**
+ * webkit_user_media_permission_is_for_display_device:
+ * @request: a #WebKitUserMediaPermissionRequest
+ *
+ * Returns: %TRUE if access to a display device was requested.
+ *
+ * Since: 2.34
+ */
+gboolean webkit_user_media_permission_is_for_display_device(WebKitUserMediaPermissionRequest* request)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(request), FALSE);
+    return request->priv->request->requiresDisplayCapture();
+}
+
 static void webkitUserMediaPermissionRequestGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec)
 {
     WebKitUserMediaPermissionRequest* request = WEBKIT_USER_MEDIA_PERMISSION_REQUEST(object);

Modified: trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp	2021-07-22 10:54:07 UTC (rev 280172)
@@ -207,6 +207,10 @@
     PROP_WEBSITE_POLICIES,
     PROP_IS_WEB_PROCESS_RESPONSIVE,
 
+    PROP_CAMERA_CAPTURE_STATE,
+    PROP_MICROPHONE_CAPTURE_STATE,
+    PROP_DISPLAY_CAPTURE_STATE,
+
     N_PROPERTIES,
 };
 
@@ -352,6 +356,22 @@
     g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_IS_PLAYING_AUDIO]);
 }
 
+void webkitWebViewMediaCaptureStateDidChange(WebKitWebView* webView, WebCore::MediaProducer::MediaStateFlags mediaStateFlags)
+{
+    if (mediaStateFlags.isEmpty()) {
+        g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_CAMERA_CAPTURE_STATE]);
+        g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_DISPLAY_CAPTURE_STATE]);
+        g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_MICROPHONE_CAPTURE_STATE]);
+        return;
+    }
+    if (mediaStateFlags.containsAny(WebCore::MediaProducer::AudioCaptureMask))
+        g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_MICROPHONE_CAPTURE_STATE]);
+    if (mediaStateFlags.containsAny(WebCore::MediaProducer::VideoCaptureMask))
+        g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_CAMERA_CAPTURE_STATE]);
+    if (mediaStateFlags.containsAny(WebCore::MediaProducer::DisplayCaptureMask))
+        g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_DISPLAY_CAPTURE_STATE]);
+}
+
 class PageLoadStateObserver final : public PageLoadState::Observer {
     WTF_MAKE_FAST_ALLOCATED;
 public:
@@ -872,6 +892,15 @@
     case PROP_WEBSITE_POLICIES:
         webView->priv->websitePolicies = static_cast<WebKitWebsitePolicies*>(g_value_get_object(value));
         break;
+    case PROP_CAMERA_CAPTURE_STATE:
+        webkit_web_view_set_camera_capture_state(webView, static_cast<WebKitMediaCaptureState>(g_value_get_enum(value)));
+        break;
+    case PROP_MICROPHONE_CAPTURE_STATE:
+        webkit_web_view_set_microphone_capture_state(webView, static_cast<WebKitMediaCaptureState>(g_value_get_enum(value)));
+        break;
+    case PROP_DISPLAY_CAPTURE_STATE:
+        webkit_web_view_set_display_capture_state(webView, static_cast<WebKitMediaCaptureState>(g_value_get_enum(value)));
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
     }
@@ -943,6 +972,15 @@
     case PROP_IS_WEB_PROCESS_RESPONSIVE:
         g_value_set_boolean(value, webkit_web_view_get_is_web_process_responsive(webView));
         break;
+    case PROP_CAMERA_CAPTURE_STATE:
+        g_value_set_enum(value, webkit_web_view_get_camera_capture_state(webView));
+        break;
+    case PROP_MICROPHONE_CAPTURE_STATE:
+        g_value_set_enum(value, webkit_web_view_get_microphone_capture_state(webView));
+        break;
+    case PROP_DISPLAY_CAPTURE_STATE:
+        g_value_set_enum(value, webkit_web_view_get_display_capture_state(webView));
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
     }
@@ -1337,6 +1375,81 @@
             TRUE,
             WEBKIT_PARAM_READABLE);
 
+    /**
+     * WebKitWebView:camera-capture-state:
+     *
+     * Capture state of the camera device. Whenever the user grants a media-request sent by the web
+     * page, requesting video capture capabilities (`navigator.mediaDevices.getUserMedia({video:
+     * true})`) this property will be set to %WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE.
+     *
+     * The application can monitor this property and provide a visual indicator allowing to optionally
+     * deactivate or mute the capture device by setting this property respectively to
+     * %WEBKIT_MEDIA_CAPTURE_STATE_NONE or %WEBKIT_MEDIA_CAPTURE_STATE_MUTED.
+     *
+     * If the capture state of the device is set to %WEBKIT_MEDIA_CAPTURE_STATE_NONE the web-page
+     * can still re-request the permission to the user. Permission desision caching is left to the
+     * application.
+     *
+     * Since: 2.34
+     */
+    sObjProperties[PROP_CAMERA_CAPTURE_STATE] = g_param_spec_enum(
+        "camera-capture-state",
+        "Camera Capture State",
+        _("The capture state of the camera device"),
+        WEBKIT_TYPE_MEDIA_CAPTURE_STATE,
+        WEBKIT_MEDIA_CAPTURE_STATE_NONE,
+        WEBKIT_PARAM_READWRITE);
+
+    /**
+     * WebKitWebView:microphone-capture-state:
+     *
+     * Capture state of the microphone device. Whenever the user grants a media-request sent by the web
+     * page, requesting audio capture capabilities (`navigator.mediaDevices.getUserMedia({audio:
+     * true})`) this property will be set to %WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE.
+     *
+     * The application can monitor this property and provide a visual indicator allowing to
+     * optionally deactivate or mute the capture device by setting this property respectively to
+     * %WEBKIT_MEDIA_CAPTURE_STATE_NONE or %WEBKIT_MEDIA_CAPTURE_STATE_MUTED.
+     *
+     * If the capture state of the device is set to %WEBKIT_MEDIA_CAPTURE_STATE_NONE the web-page
+     * can still re-request the permission to the user. Permission desision caching is left to the
+     * application.
+     *
+     * Since: 2.34
+     */
+    sObjProperties[PROP_MICROPHONE_CAPTURE_STATE] = g_param_spec_enum(
+        "microphone-capture-state",
+        "Microphone Capture State",
+        _("The capture state of the microphone device"),
+        WEBKIT_TYPE_MEDIA_CAPTURE_STATE,
+        WEBKIT_MEDIA_CAPTURE_STATE_NONE,
+        WEBKIT_PARAM_READWRITE);
+
+    /**
+     * WebKitWebView:display-capture-state:
+     *
+     * Capture state of the display device. Whenever the user grants a media-request sent by the web
+     * page, requesting screencasting capabilities (`navigator.mediaDevices.getDisplayMedia() this
+     * property will be set to %WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE.
+     *
+     * The application can monitor this property and provide a visual indicator allowing to
+     * optionally deactivate or mute the capture device by setting this property respectively to
+     * %WEBKIT_MEDIA_CAPTURE_STATE_NONE or %WEBKIT_MEDIA_CAPTURE_STATE_MUTED.
+     *
+     * If the capture state of the device is set to %WEBKIT_MEDIA_CAPTURE_STATE_NONE the web-page
+     * can still re-request the permission to the user. Permission desision caching is left to the
+     * application.
+     *
+     * Since: 2.34
+     */
+    sObjProperties[PROP_DISPLAY_CAPTURE_STATE] = g_param_spec_enum(
+        "display-capture-state",
+        "Display Capture State",
+        _("The capture state of the display device"),
+        WEBKIT_TYPE_MEDIA_CAPTURE_STATE,
+        WEBKIT_MEDIA_CAPTURE_STATE_NONE,
+        WEBKIT_PARAM_READWRITE);
+
     g_object_class_install_properties(gObjectClass, N_PROPERTIES, sObjProperties);
 
     /**
@@ -4230,10 +4343,10 @@
  * when it's emitted with %WEBKIT_LOAD_COMMITTED event.
  *
  * Note that this function provides no information about the security of the web
- * page if the current #WebKitTLSErrorsPolicy is @WEBKIT_TLS_ERRORS_POLICY_IGNORE,
+ * page if the current #WebKitTLSErrorsPolicy is %WEBKIT_TLS_ERRORS_POLICY_IGNORE,
  * as subresources of the page may be controlled by an attacker. This function
  * may safely be used to determine the security status of the current page only
- * if the current #WebKitTLSErrorsPolicy is @WEBKIT_TLS_ERRORS_POLICY_FAIL, in
+ * if the current #WebKitTLSErrorsPolicy is %WEBKIT_TLS_ERRORS_POLICY_FAIL, in
  * which case subresources that fail certificate verification will be blocked.
  *
  * Returns: %TRUE if the @web_view connection uses HTTPS and a response has been received
@@ -4754,3 +4867,187 @@
 
     getPage(webView).setCORSDisablingPatterns(WTFMove(allowListVector));
 }
+
+static void webkitWebViewConfigureMediaCapture(WebKitWebView* webView, WebCore::MediaProducer::MediaCaptureKind captureKind, WebKitMediaCaptureState captureState, bool isFromDisplayCapture = false)
+{
+    auto& page = getPage(webView);
+    auto mutedState = page.mutedStateFlags();
+
+    switch (captureState) {
+    case WEBKIT_MEDIA_CAPTURE_STATE_NONE:
+        page.stopMediaCapture(captureKind, [webView, captureKind] {
+            switch (captureKind) {
+            case WebCore::MediaProducer::MediaCaptureKind::Audio:
+                g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_MICROPHONE_CAPTURE_STATE]);
+                break;
+            case WebCore::MediaProducer::MediaCaptureKind::Video:
+                g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_CAMERA_CAPTURE_STATE]);
+                break;
+            case WebCore::MediaProducer::MediaCaptureKind::AudioVideo:
+                ASSERT_NOT_REACHED();
+                return;
+            }
+        });
+        break;
+    case WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE:
+        switch (captureKind) {
+        case WebCore::MediaProducer::MediaCaptureKind::Audio:
+            mutedState.remove(WebCore::MediaProducer::MutedState::AudioCaptureIsMuted);
+            break;
+        case WebCore::MediaProducer::MediaCaptureKind::Video:
+            if (isFromDisplayCapture)
+                mutedState.remove(WebCore::MediaProducer::MutedState::ScreenCaptureIsMuted);
+            else
+                mutedState.remove(WebCore::MediaProducer::MutedState::VideoCaptureIsMuted);
+            break;
+        case WebCore::MediaProducer::MediaCaptureKind::AudioVideo:
+            ASSERT_NOT_REACHED();
+            return;
+        }
+        page.setMuted(mutedState);
+        break;
+    case WEBKIT_MEDIA_CAPTURE_STATE_MUTED:
+        switch (captureKind) {
+        case WebCore::MediaProducer::MediaCaptureKind::Audio:
+            mutedState.add(WebCore::MediaProducer::MutedState::AudioCaptureIsMuted);
+            break;
+        case WebCore::MediaProducer::MediaCaptureKind::Video:
+            if (isFromDisplayCapture)
+                mutedState.add(WebCore::MediaProducer::MutedState::ScreenCaptureIsMuted);
+            else
+                mutedState.add(WebCore::MediaProducer::MutedState::VideoCaptureIsMuted);
+            break;
+        case WebCore::MediaProducer::MediaCaptureKind::AudioVideo:
+            ASSERT_NOT_REACHED();
+            return;
+        }
+        page.setMuted(mutedState);
+        break;
+    }
+}
+
+/**
+ * webkit_web_view_get_camera_capture_state:
+ * @web_view: a #WebKitWebView
+ *
+ * Get the camera capture state of a #WebKitWebView.
+ *
+ * Returns: The #WebKitMediaCaptureState of the camera device. If #WebKitSettings:enable-mediastream
+ * is %FALSE, this method will return %WEBKIT_MEDIA_CAPTURE_STATE_NONE.
+ *
+ * Since: 2.34
+ */
+WebKitMediaCaptureState webkit_web_view_get_camera_capture_state(WebKitWebView* webView)
+{
+    auto state = getPage(webView).reportedMediaState();
+    if (state & WebCore::MediaProducer::MediaState::HasActiveVideoCaptureDevice)
+        return WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE;
+    if (state & WebCore::MediaProducer::MediaState::HasMutedVideoCaptureDevice)
+        return WEBKIT_MEDIA_CAPTURE_STATE_MUTED;
+    return WEBKIT_MEDIA_CAPTURE_STATE_NONE;
+}
+
+/**
+ * webkit_web_view_set_camera_capture_state:
+ * @web_view: a #WebKitWebView
+ * @state: a #WebKitMediaCaptureState
+ *
+ * Set the camera capture state of a #WebKitWebView.
+ *
+ * If #WebKitSettings:enable-mediastream is %FALSE, this method will have no visible effect. Once the
+ * state of the device has been set to %WEBKIT_MEDIA_CAPTURE_STATE_NONE it cannot be changed
+ * anymore. The page can however request capture again using the mediaDevices API.
+ *
+ * Since: 2.34
+ */
+void webkit_web_view_set_camera_capture_state(WebKitWebView* webView, WebKitMediaCaptureState state)
+{
+    if (webkit_web_view_get_camera_capture_state(webView) == WEBKIT_MEDIA_CAPTURE_STATE_NONE)
+        return;
+
+    webkitWebViewConfigureMediaCapture(webView, WebCore::MediaProducer::MediaCaptureKind::Video, state);
+}
+
+/**
+ * webkit_web_view_get_microphone_capture_state:
+ * @web_view: a #WebKitWebView
+ *
+ * Get the microphone capture state of a #WebKitWebView.
+ *
+ * Returns: The #WebKitMediaCaptureState of the microphone device. If #WebKitSettings:enable-mediastream
+ * is %FALSE, this method will return %WEBKIT_MEDIA_CAPTURE_STATE_NONE.
+ *
+ * Since: 2.34
+ */
+WebKitMediaCaptureState webkit_web_view_get_microphone_capture_state(WebKitWebView* webView)
+{
+    auto state = getPage(webView).reportedMediaState();
+    if (state & WebCore::MediaProducer::MediaState::HasActiveAudioCaptureDevice)
+        return WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE;
+    if (state & WebCore::MediaProducer::MediaState::HasMutedAudioCaptureDevice)
+        return WEBKIT_MEDIA_CAPTURE_STATE_MUTED;
+    return WEBKIT_MEDIA_CAPTURE_STATE_NONE;
+}
+
+/**
+ * webkit_web_view_set_microphone_capture_state:
+ * @web_view: a #WebKitWebView
+ * @state: a #WebKitMediaCaptureState
+ *
+ * Set the microphone capture state of a #WebKitWebView.
+ *
+ * If #WebKitSettings:enable-mediastream is %FALSE, this method will have no visible effect. Once the
+ * state of the device has been set to %WEBKIT_MEDIA_CAPTURE_STATE_NONE it cannot be changed
+ * anymore. The page can however request capture again using the mediaDevices API.
+ *
+ * Since: 2.34
+ */
+void webkit_web_view_set_microphone_capture_state(WebKitWebView* webView, WebKitMediaCaptureState state)
+{
+    if (webkit_web_view_get_microphone_capture_state(webView) == WEBKIT_MEDIA_CAPTURE_STATE_NONE)
+        return;
+
+    webkitWebViewConfigureMediaCapture(webView, WebCore::MediaProducer::MediaCaptureKind::Audio, state);
+}
+
+/**
+ * webkit_web_view_get_display_capture_state:
+ * @web_view: a #WebKitWebView
+ *
+ * Get the display capture state of a #WebKitWebView.
+ *
+ * Returns: The #WebKitMediaCaptureState of the display device. If #WebKitSettings:enable-mediastream
+ * is %FALSE, this method will return %WEBKIT_MEDIA_CAPTURE_STATE_NONE.
+ *
+ * Since: 2.34
+ */
+WebKitMediaCaptureState webkit_web_view_get_display_capture_state(WebKitWebView* webView)
+{
+    auto state = getPage(webView).reportedMediaState();
+    if (state & WebCore::MediaProducer::MediaState::HasActiveDisplayCaptureDevice)
+        return WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE;
+    if (state & WebCore::MediaProducer::MediaState::HasMutedDisplayCaptureDevice)
+        return WEBKIT_MEDIA_CAPTURE_STATE_MUTED;
+    return WEBKIT_MEDIA_CAPTURE_STATE_NONE;
+}
+
+/**
+ * webkit_web_view_set_display_capture_state:
+ * @web_view: a #WebKitWebView
+ * @state: a #WebKitMediaCaptureState
+ *
+ * Set the display capture state of a #WebKitWebView.
+ *
+ * If #WebKitSettings:enable-mediastream is %FALSE, this method will have no visible effect. Once the
+ * state of the device has been set to %WEBKIT_MEDIA_CAPTURE_STATE_NONE it cannot be changed
+ * anymore. The page can however request capture again using the mediaDevices API.
+ *
+ * Since: 2.34
+ */
+void webkit_web_view_set_display_capture_state(WebKitWebView* webView, WebKitMediaCaptureState state)
+{
+    if (webkit_web_view_get_display_capture_state(webView) == WEBKIT_MEDIA_CAPTURE_STATE_NONE)
+        return;
+
+    webkitWebViewConfigureMediaCapture(webView, WebCore::MediaProducer::MediaCaptureKind::Video, state, true);
+}

Modified: trunk/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h	2021-07-22 10:54:07 UTC (rev 280172)
@@ -91,6 +91,7 @@
 bool webkitWebViewEmitShowNotification(WebKitWebView*, WebKitNotification*);
 void webkitWebViewWebProcessTerminated(WebKitWebView*, WebKitWebProcessTerminationReason);
 void webkitWebViewIsPlayingAudioChanged(WebKitWebView*);
+void webkitWebViewMediaCaptureStateDidChange(WebKitWebView*, WebCore::MediaProducer::MediaStateFlags);
 void webkitWebViewSelectionDidChange(WebKitWebView*);
 void webkitWebViewRequestInstallMissingMediaPlugins(WebKitWebView*, WebKit::InstallMissingMediaPluginsPermissionRequest&);
 WebKitWebsiteDataManager* webkitWebViewGetWebsiteDataManager(WebKitWebView*);

Modified: trunk/Source/WebKit/UIProcess/API/gtk/WebKitUserMediaPermissionRequest.h (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/gtk/WebKitUserMediaPermissionRequest.h	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/gtk/WebKitUserMediaPermissionRequest.h	2021-07-22 10:54:07 UTC (rev 280172)
@@ -64,6 +64,9 @@
 WEBKIT_API gboolean
 webkit_user_media_permission_is_for_video_device (WebKitUserMediaPermissionRequest *request);
 
+WEBKIT_API gboolean
+webkit_user_media_permission_is_for_display_device (WebKitUserMediaPermissionRequest *request);
+
 G_END_DECLS
 
 #endif

Modified: trunk/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h	2021-07-22 10:54:07 UTC (rev 280172)
@@ -205,6 +205,22 @@
     WEBKIT_WEB_PROCESS_TERMINATED_BY_API
 } WebKitWebProcessTerminationReason;
 
+/**
+ * WebKitMediaCaptureState:
+ * @WEBKIT_MEDIA_CAPTURE_STATE_NONE: Media capture is disabled.
+ * @WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE: Media capture is active.
+ * @WEBKIT_MEDIA_CAPTURE_STATE_MUTED: Media capture is muted.
+ *
+ * Enum values used to specify the capture state of a media device.
+ *
+ * Since: 2.34
+ */
+typedef enum {
+    WEBKIT_MEDIA_CAPTURE_STATE_NONE,
+    WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE,
+    WEBKIT_MEDIA_CAPTURE_STATE_MUTED,
+} WebKitMediaCaptureState;
+
 struct _WebKitWebView {
     WebKitWebViewBase parent;
 
@@ -600,6 +616,27 @@
 WEBKIT_API void
 webkit_web_view_terminate_web_process                (WebKitWebView             *web_view);
 
+WEBKIT_API WebKitMediaCaptureState
+webkit_web_view_get_camera_capture_state             (WebKitWebView             *web_view);
+
+WEBKIT_API void
+webkit_web_view_set_camera_capture_state             (WebKitWebView             *web_view,
+                                                      WebKitMediaCaptureState    state);
+
+WEBKIT_API WebKitMediaCaptureState
+webkit_web_view_get_microphone_capture_state         (WebKitWebView             *web_view);
+
+WEBKIT_API void
+webkit_web_view_set_microphone_capture_state         (WebKitWebView             *web_view,
+                                                      WebKitMediaCaptureState    state);
+
+WEBKIT_API WebKitMediaCaptureState
+webkit_web_view_get_display_capture_state            (WebKitWebView             *web_view);
+
+WEBKIT_API void
+webkit_web_view_set_display_capture_state            (WebKitWebView             *web_view,
+                                                      WebKitMediaCaptureState    state);
+
 G_END_DECLS
 
 #endif

Modified: trunk/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt	2021-07-22 10:54:07 UTC (rev 280172)
@@ -199,6 +199,7 @@
 WebKitSnapshotRegion
 WebKitWebProcessTerminationReason
 WebKitAutoplayPolicy
+WebKitMediaCaptureState
 
 <SUBSECTION Editing Commands>
 WEBKIT_EDITING_COMMAND_CUT
@@ -293,6 +294,12 @@
 webkit_web_view_get_is_web_process_responsive
 webkit_web_view_terminate_web_process
 webkit_web_view_set_cors_allowlist
+webkit_web_view_get_camera_capture_state
+webkit_web_view_get_microphone_capture_state
+webkit_web_view_set_camera_capture_state
+webkit_web_view_set_microphone_capture_state
+webkit_web_view_get_display_capture_state
+webkit_web_view_set_display_capture_state
 
 <SUBSECTION WebKitJavascriptResult>
 WebKitJavascriptResult
@@ -796,6 +803,7 @@
 WebKitUserMediaPermissionRequest
 webkit_user_media_permission_is_for_audio_device
 webkit_user_media_permission_is_for_video_device
+webkit_user_media_permission_is_for_display_device
 
 <SUBSECTION Standard>
 WebKitUserMediaPermissionRequestClass

Modified: trunk/Source/WebKit/UIProcess/API/wpe/WebKitUserMediaPermissionRequest.h (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/wpe/WebKitUserMediaPermissionRequest.h	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/wpe/WebKitUserMediaPermissionRequest.h	2021-07-22 10:54:07 UTC (rev 280172)
@@ -64,6 +64,9 @@
 WEBKIT_API gboolean
 webkit_user_media_permission_is_for_video_device (WebKitUserMediaPermissionRequest *request);
 
+WEBKIT_API gboolean
+webkit_user_media_permission_is_for_display_device (WebKitUserMediaPermissionRequest *request);
+
 G_END_DECLS
 
 #endif

Modified: trunk/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h	2021-07-22 10:54:07 UTC (rev 280172)
@@ -186,6 +186,22 @@
 typedef void (* WebKitFrameDisplayedCallback) (WebKitWebView *web_view,
                                                gpointer       user_data);
 
+/**
+ * WebKitMediaCaptureState:
+ * @WEBKIT_MEDIA_CAPTURE_STATE_NONE: Media capture is disabled.
+ * @WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE: Media capture is active.
+ * @WEBKIT_MEDIA_CAPTURE_STATE_MUTED: Media capture is muted.
+ *
+ * Enum values used to specify the capture state of a media device.
+ *
+ * Since: 2.34
+ */
+typedef enum {
+    WEBKIT_MEDIA_CAPTURE_STATE_NONE,
+    WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE,
+    WEBKIT_MEDIA_CAPTURE_STATE_MUTED,
+} WebKitMediaCaptureState;
+
 struct _WebKitWebView {
     GObject parent;
 
@@ -577,6 +593,27 @@
 webkit_web_view_set_cors_allowlist                   (WebKitWebView             *web_view,
                                                       const gchar * const       *allowlist);
 
+WEBKIT_API WebKitMediaCaptureState
+webkit_web_view_get_camera_capture_state             (WebKitWebView             *web_view);
+
+WEBKIT_API void
+webkit_web_view_set_camera_capture_state             (WebKitWebView             *web_view,
+                                                      WebKitMediaCaptureState    state);
+
+WEBKIT_API WebKitMediaCaptureState
+webkit_web_view_get_microphone_capture_state         (WebKitWebView             *web_view);
+
+WEBKIT_API void
+webkit_web_view_set_microphone_capture_state         (WebKitWebView             *web_view,
+                                                      WebKitMediaCaptureState    state);
+
+WEBKIT_API WebKitMediaCaptureState
+webkit_web_view_get_display_capture_state            (WebKitWebView             *web_view);
+
+WEBKIT_API void
+webkit_web_view_set_display_capture_state            (WebKitWebView             *web_view,
+                                                      WebKitMediaCaptureState    state);
+
 G_END_DECLS
 
 #endif

Modified: trunk/Source/WebKit/UIProcess/API/wpe/docs/wpe-1.0-sections.txt (280171 => 280172)


--- trunk/Source/WebKit/UIProcess/API/wpe/docs/wpe-1.0-sections.txt	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Source/WebKit/UIProcess/API/wpe/docs/wpe-1.0-sections.txt	2021-07-22 10:54:07 UTC (rev 280172)
@@ -173,6 +173,7 @@
 WebKitWebProcessTerminationReason
 WebKitFrameDisplayedCallback
 WebKitAutoplayPolicy
+WebKitMediaCaptureState
 
 <SUBSECTION Editing Commands>
 WEBKIT_EDITING_COMMAND_CUT
@@ -265,6 +266,12 @@
 webkit_web_view_get_is_web_process_responsive
 webkit_web_view_terminate_web_process
 webkit_web_view_set_cors_allowlist
+webkit_web_view_get_camera_capture_state
+webkit_web_view_get_microphone_capture_state
+webkit_web_view_set_camera_capture_state
+webkit_web_view_set_microphone_capture_state
+webkit_web_view_get_display_capture_state
+webkit_web_view_set_display_capture_state
 
 <SUBSECTION WebKitJavascriptResult>
 WebKitJavascriptResult
@@ -800,6 +807,7 @@
 WebKitUserMediaPermissionRequest
 webkit_user_media_permission_is_for_audio_device
 webkit_user_media_permission_is_for_video_device
+webkit_user_media_permission_is_for_display_device
 
 <SUBSECTION Standard>
 WebKitUserMediaPermissionRequestClass

Modified: trunk/Tools/ChangeLog (280171 => 280172)


--- trunk/Tools/ChangeLog	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Tools/ChangeLog	2021-07-22 10:54:07 UTC (rev 280172)
@@ -1,3 +1,35 @@
+2021-07-22  Philippe Normand  <[email protected]>
+
+        [GLib] Expose API to access/modify capture devices states
+        https://bugs.webkit.org/show_bug.cgi?id=227902
+
+        Reviewed by Carlos Garcia Campos.
+
+        The GTK MiniBrowser is now able to show media capture indicators, through the URI entry. A
+        new API test was added, checking support for getDisplayMedia().
+
+        * MiniBrowser/gtk/BrowserTab.c:
+        (decidePermissionRequest):
+        * MiniBrowser/gtk/BrowserWindow.c:
+        (webViewMediaCaptureStateChanged):
+        (webViewUriEntryIconPressed):
+        (browserWindowSwitchTab):
+        * TestWebKitAPI/Tests/WebKitGLib/TestUIClient.cpp:
+        (testWebViewUserMediaPermissionRequests):
+        (testWebViewAudioOnlyUserMediaPermissionRequests):
+        (testWebViewDisplayUserMediaPermissionRequests):
+        (beforeAll):
+        * TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp:
+        (displayCaptureChanged):
+        (WebViewTest::waitUntilDisplayCaptureStateChangedTo):
+        (microphoneCaptureChanged):
+        (WebViewTest::waitUntilMicrophoneCaptureStateChangedTo):
+        (cameraCaptureChanged):
+        (WebViewTest::waitUntilCameraCaptureStateChangedTo):
+        * TestWebKitAPI/glib/WebKitGLib/WebViewTest.h:
+        * flatpak/flatpakutils.py:
+        (WebkitFlatpak.run_in_sandbox):
+
 2021-07-21  Alexey Proskuryakov  <[email protected]>
 
         MiniBrowser xcconfig should include ccache.xcconfig conditionally

Modified: trunk/Tools/MiniBrowser/gtk/BrowserTab.c (280171 => 280172)


--- trunk/Tools/MiniBrowser/gtk/BrowserTab.c	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Tools/MiniBrowser/gtk/BrowserTab.c	2021-07-22 10:54:07 UTC (rev 280172)
@@ -307,16 +307,20 @@
         text = g_strdup("Allow notifications request?");
     } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(request)) {
         title = "UserMedia request";
-        gboolean is_for_audio_device = webkit_user_media_permission_is_for_audio_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request));
-        gboolean is_for_video_device = webkit_user_media_permission_is_for_video_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request));
+        gboolean isForAudioDevice = webkit_user_media_permission_is_for_audio_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request));
+        gboolean isForVideoDevice = webkit_user_media_permission_is_for_video_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request));
+        gboolean isForDisplayDevice = webkit_user_media_permission_is_for_display_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request));
+
         const char *mediaType = NULL;
-        if (is_for_audio_device) {
-            if (is_for_video_device)
+        if (isForAudioDevice) {
+            if (isForVideoDevice)
                 mediaType = "audio/video";
             else
                 mediaType = "audio";
-        } else if (is_for_video_device)
+        } else if (isForVideoDevice)
             mediaType = "video";
+        else if (isForDisplayDevice)
+            mediaType = "display";
         text = g_strdup_printf("Allow access to %s device?", mediaType);
     } else if (WEBKIT_IS_INSTALL_MISSING_MEDIA_PLUGINS_PERMISSION_REQUEST(request)) {
         title = "Media plugin missing request";

Modified: trunk/Tools/MiniBrowser/gtk/BrowserWindow.c (280171 => 280172)


--- trunk/Tools/MiniBrowser/gtk/BrowserWindow.c	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Tools/MiniBrowser/gtk/BrowserWindow.c	2021-07-22 10:54:07 UTC (rev 280172)
@@ -649,6 +649,72 @@
     updateUriEntryIcon(window);
 }
 
+static void webViewMediaCaptureStateChanged(WebKitWebView* webView, GParamSpec* paramSpec, BrowserWindow* window)
+{
+    const gchar* name = g_param_spec_get_name(paramSpec);
+    // FIXME: the URI entry is not great storage in case more than one capture device is in use,
+    // because it can store only one secondary icon.
+    GtkEntry *entry = GTK_ENTRY(window->uriEntry);
+
+    if (g_str_has_prefix(name, "microphone")) {
+        switch (webkit_web_view_get_microphone_capture_state(webView)) {
+        case WEBKIT_MEDIA_CAPTURE_STATE_NONE:
+            gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+            break;
+        case WEBKIT_MEDIA_CAPTURE_STATE_MUTED:
+            gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, "microphone-sensivity-mutes-symbolic");
+            break;
+        case WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE:
+            gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, "audio-input-microphone-symbolic");
+            break;
+        }
+    } else if (g_str_has_prefix(name, "camera")) {
+        switch (webkit_web_view_get_camera_capture_state(webView)) {
+        case WEBKIT_MEDIA_CAPTURE_STATE_NONE:
+            gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+            break;
+        case WEBKIT_MEDIA_CAPTURE_STATE_MUTED:
+            gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, "camera-disabled-symbolic");
+            break;
+        case WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE:
+            gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, "camera-web-symbolic");
+            break;
+        }
+    } else if (g_str_has_prefix(name, "display")) {
+        switch (webkit_web_view_get_display_capture_state(webView)) {
+        case WEBKIT_MEDIA_CAPTURE_STATE_NONE:
+            gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+            break;
+        case WEBKIT_MEDIA_CAPTURE_STATE_MUTED:
+            // FIXME: I found no suitable icon for this.
+            gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, "media-playback-stop-symbolic");
+            break;
+        case WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE:
+            gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, "video-display-symbolic");
+            break;
+        }
+    }
+}
+
+static void webViewUriEntryIconPressed(GtkEntry* entry, GtkEntryIconPosition position, GdkEvent* event, BrowserWindow* window)
+{
+    if (position != GTK_ENTRY_ICON_SECONDARY)
+        return;
+
+    // FIXME: What about audio/video?
+    WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+    switch (webkit_web_view_get_display_capture_state(webView)) {
+    case WEBKIT_MEDIA_CAPTURE_STATE_NONE:
+        break;
+    case WEBKIT_MEDIA_CAPTURE_STATE_MUTED:
+        webkit_web_view_set_display_capture_state(webView, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+        break;
+    case WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE:
+        webkit_web_view_set_display_capture_state(webView, WEBKIT_MEDIA_CAPTURE_STATE_MUTED);
+        break;
+    }
+}
+
 static void webViewIsLoadingChanged(WebKitWebView *webView, GParamSpec *paramSpec, BrowserWindow *window)
 {
     gboolean isLoading = webkit_web_view_is_loading(webView);
@@ -1180,7 +1246,13 @@
 #if !GTK_CHECK_VERSION(3, 98, 0)
     g_signal_connect(webView, "scroll-event", G_CALLBACK(scrollEventCallback), window);
 #endif
+    g_signal_connect_object(webView, "notify::camera-capture-state", G_CALLBACK(webViewMediaCaptureStateChanged), window, 0);
+    g_signal_connect_object(webView, "notify::microphone-capture-state", G_CALLBACK(webViewMediaCaptureStateChanged), window, 0);
+    g_signal_connect_object(webView, "notify::display-capture-state", G_CALLBACK(webViewMediaCaptureStateChanged), window, 0);
 
+    g_object_set(window->uriEntry, "secondary-icon-activatable", TRUE, NULL);
+    g_signal_connect(window->uriEntry, "icon-press", G_CALLBACK(webViewUriEntryIconPressed), window);
+
     WebKitBackForwardList *backForwardlist = webkit_web_view_get_back_forward_list(webView);
     browserWindowUpdateNavigationMenu(window, backForwardlist);
     g_signal_connect(backForwardlist, "changed", G_CALLBACK(backForwardlistChanged), window);

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestUIClient.cpp (280171 => 280172)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestUIClient.cpp	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestUIClient.cpp	2021-07-22 10:54:07 UTC (rev 280172)
@@ -240,6 +240,7 @@
             WebKitUserMediaPermissionRequest* userMediaRequest = WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request);
             g_assert_true(webkit_user_media_permission_is_for_audio_device(userMediaRequest) == test->m_expectedAudioMedia);
             g_assert_true(webkit_user_media_permission_is_for_video_device(userMediaRequest) == test->m_expectedVideoMedia);
+            g_assert_true(webkit_user_media_permission_is_for_display_device(userMediaRequest) == test->m_expectedDisplayMedia);
         }
 
         if (test->m_allowPermissionRequests)
@@ -256,6 +257,61 @@
         g_main_loop_quit(test->m_mainLoop);
     }
 
+
+    static void displayCaptureChanged(WebKitWebView* webView, GParamSpec*, UIClientTest* test)
+    {
+        if (test->m_expectedDisplayCaptureState && *test->m_expectedDisplayCaptureState != webkit_web_view_get_display_capture_state(webView))
+            return;
+
+        g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(displayCaptureChanged), test);
+        g_main_loop_quit(test->m_mainLoop);
+    }
+
+    void waitUntilDisplayCaptureStateChangedTo(WebKitMediaCaptureState expectedCaptureState)
+    {
+        *m_expectedDisplayCaptureState = expectedCaptureState;
+        g_signal_connect(m_webView, "notify::display-capture-state", G_CALLBACK(displayCaptureChanged), this);
+        g_main_loop_run(m_mainLoop);
+        g_assert_cmpuint(webkit_web_view_get_display_capture_state(m_webView), ==, expectedCaptureState);
+        m_expectedDisplayCaptureState.reset();
+    }
+
+    static void microphoneCaptureChanged(WebKitWebView* webView, GParamSpec*, UIClientTest* test)
+    {
+        if (test->m_expectedMicrophoneCaptureState && *test->m_expectedMicrophoneCaptureState != webkit_web_view_get_microphone_capture_state(webView))
+            return;
+
+        g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(microphoneCaptureChanged), test);
+        g_main_loop_quit(test->m_mainLoop);
+    }
+
+    void waitUntilMicrophoneCaptureStateChangedTo(WebKitMediaCaptureState expectedCaptureState)
+    {
+        *m_expectedMicrophoneCaptureState = expectedCaptureState;
+        g_signal_connect(m_webView, "notify::microphone-capture-state", G_CALLBACK(microphoneCaptureChanged), this);
+        g_main_loop_run(m_mainLoop);
+        g_assert_cmpuint(webkit_web_view_get_microphone_capture_state(m_webView), ==, expectedCaptureState);
+        m_expectedMicrophoneCaptureState.reset();
+    }
+
+    static void cameraCaptureChanged(WebKitWebView* webView, GParamSpec*, UIClientTest* test)
+    {
+        if (test->m_expectedCameraCaptureState && *test->m_expectedCameraCaptureState != webkit_web_view_get_camera_capture_state(webView))
+            return;
+
+        g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(cameraCaptureChanged), test);
+        g_main_loop_quit(test->m_mainLoop);
+    }
+
+    void waitUntilCameraCaptureStateChangedTo(WebKitMediaCaptureState expectedCaptureState)
+    {
+        *m_expectedCameraCaptureState = expectedCaptureState;
+        g_signal_connect(m_webView, "notify::camera-capture-state", G_CALLBACK(cameraCaptureChanged), this);
+        g_main_loop_run(m_mainLoop);
+        g_assert_cmpuint(webkit_web_view_get_camera_capture_state(m_webView), ==, expectedCaptureState);
+        m_expectedCameraCaptureState.reset();
+    }
+
     UIClientTest()
         : m_scriptDialogType(WEBKIT_SCRIPT_DIALOG_ALERT)
         , m_scriptDialogConfirmed(true)
@@ -263,6 +319,7 @@
         , m_verifyMediaTypes(false)
         , m_expectedAudioMedia(false)
         , m_expectedVideoMedia(false)
+        , m_expectedDisplayMedia(false)
         , m_mouseTargetModifiers(0)
     {
         webkit_settings_set_javascript_can_open_windows_automatically(webkit_web_view_get_settings(m_webView), TRUE);
@@ -415,6 +472,7 @@
     gboolean m_verifyMediaTypes;
     gboolean m_expectedAudioMedia;
     gboolean m_expectedVideoMedia;
+    gboolean m_expectedDisplayMedia;
     WindowProperties m_windowProperties;
     HashSet<WTF::String> m_windowPropertiesChanged;
     GRefPtr<WebKitHitTestResult> m_mouseTargetHitTestResult;
@@ -426,6 +484,10 @@
     bool m_shouldCreateWebViewsInNewWindowsAutomatically { false };
     cairo_rectangle_int_t m_defaultGeometryNewWindows;
 #endif
+
+    std::optional<WebKitMediaCaptureState> m_expectedCameraCaptureState;
+    std::optional<WebKitMediaCaptureState> m_expectedDisplayCaptureState;
+    std::optional<WebKitMediaCaptureState> m_expectedMicrophoneCaptureState;
 };
 
 static void testWebViewCreateReadyClose(UIClientTest* test, gconstpointer)
@@ -1001,17 +1063,45 @@
     test->m_verifyMediaTypes = TRUE;
     test->m_expectedAudioMedia = TRUE;
     test->m_expectedVideoMedia = TRUE;
+    test->m_expectedDisplayMedia = FALSE;
 
     // Test denying a permission request.
     test->m_allowPermissionRequests = false;
     test->loadHtml(userMediaRequestHTML, nullptr);
     test->waitUntilTitleChangedTo("NotAllowedError");
+    g_assert_cmpuint(webkit_web_view_get_display_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    g_assert_cmpuint(webkit_web_view_get_microphone_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    g_assert_cmpuint(webkit_web_view_get_camera_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
 
     // Test allowing a permission request.
     test->m_allowPermissionRequests = true;
     test->loadHtml(userMediaRequestHTML, nullptr);
     test->waitUntilTitleChangedTo("OK");
+    g_assert_cmpuint(webkit_web_view_get_display_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    g_assert_cmpuint(webkit_web_view_get_microphone_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+    g_assert_cmpuint(webkit_web_view_get_camera_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
 
+    webkit_web_view_set_microphone_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_MUTED);
+    test->waitUntilMicrophoneCaptureStateChangedTo(WEBKIT_MEDIA_CAPTURE_STATE_MUTED);
+
+    webkit_web_view_set_microphone_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+    test->waitUntilMicrophoneCaptureStateChangedTo(WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+
+    webkit_web_view_set_camera_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_MUTED);
+    test->waitUntilCameraCaptureStateChangedTo(WEBKIT_MEDIA_CAPTURE_STATE_MUTED);
+
+    webkit_web_view_set_camera_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+    test->waitUntilCameraCaptureStateChangedTo(WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+
+    webkit_web_view_set_camera_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    test->waitUntilCameraCaptureStateChangedTo(WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+
+    // A de-activated capture device cannot be re-activated.
+    webkit_web_view_set_camera_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+    g_assert_cmpuint(webkit_web_view_get_camera_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+
+    g_assert_cmpuint(webkit_web_view_get_microphone_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+
     webkit_settings_set_enable_media_stream(settings, enabled);
     webkit_settings_set_enable_mock_capture_devices(settings, FALSE);
     webkitSettingsSetMediaCaptureRequiresSecureConnection(settings, TRUE);
@@ -1041,16 +1131,86 @@
     test->m_verifyMediaTypes = TRUE;
     test->m_expectedAudioMedia = TRUE;
     test->m_expectedVideoMedia = FALSE;
+    test->m_expectedDisplayMedia = FALSE;
 
     // Test denying a permission request.
     test->m_allowPermissionRequests = false;
     test->loadHtml(userMediaRequestHTML, nullptr);
     test->waitUntilTitleChangedTo("NotAllowedError");
+    g_assert_cmpuint(webkit_web_view_get_display_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    g_assert_cmpuint(webkit_web_view_get_microphone_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    g_assert_cmpuint(webkit_web_view_get_camera_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
 
     webkit_settings_set_enable_media_stream(settings, enabled);
     webkit_settings_set_enable_mock_capture_devices(settings, FALSE);
     webkitSettingsSetMediaCaptureRequiresSecureConnection(settings, TRUE);
 }
+
+static void testWebViewDisplayUserMediaPermissionRequests(UIClientTest* test, gconstpointer)
+{
+    WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView);
+    gboolean enabled = webkit_settings_get_enable_media_stream(settings);
+    webkit_settings_set_enable_media_stream(settings, TRUE);
+    webkit_settings_set_enable_mock_capture_devices(settings, TRUE);
+    webkitSettingsSetMediaCaptureRequiresSecureConnection(settings, FALSE);
+
+    test->showInWindow();
+    static const char* displayMediaRequestHTML = "<html>"
+        "  <script>"
+        "  function runTest()"
+        "  {"
+        "      navigator.mediaDevices.getDisplayMedia()"
+        "        .then((stream) => { document.title = \"OK\"; })"
+        "        .catch((e) => { document.title = e.name; });"
+        "  }"
+        "  </script>"
+        "  <body>"
+        "  <input style=\"position:absolute; left:0; top:0; margin:0; padding:0\" type=\"button\" value=\"click me\" _onclick_=\"runTest();\"/>"
+        "  </body>"
+        "</html>";
+
+    test->m_verifyMediaTypes = TRUE;
+    test->m_expectedAudioMedia = FALSE;
+    test->m_expectedVideoMedia = FALSE;
+    test->m_expectedDisplayMedia = TRUE;
+
+    // Test denying a permission request.
+    test->m_allowPermissionRequests = false;
+    test->loadHtml(displayMediaRequestHTML, nullptr);
+    test->waitUntilLoadFinished();
+    test->clickMouseButton(5, 5);
+    test->waitUntilTitleChangedTo("NotAllowedError");
+    g_assert_cmpuint(webkit_web_view_get_display_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    g_assert_cmpuint(webkit_web_view_get_microphone_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    g_assert_cmpuint(webkit_web_view_get_camera_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+
+    // Test allowing a permission request.
+    test->m_allowPermissionRequests = true;
+    test->loadHtml(displayMediaRequestHTML, nullptr);
+    test->waitUntilLoadFinished();
+    test->clickMouseButton(5, 5);
+    test->waitUntilTitleChangedTo("OK");
+    g_assert_cmpuint(webkit_web_view_get_display_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+    g_assert_cmpuint(webkit_web_view_get_microphone_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    g_assert_cmpuint(webkit_web_view_get_camera_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+
+    webkit_web_view_set_display_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_MUTED);
+    test->waitUntilDisplayCaptureStateChangedTo(WEBKIT_MEDIA_CAPTURE_STATE_MUTED);
+
+    webkit_web_view_set_display_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+    test->waitUntilDisplayCaptureStateChangedTo(WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+
+    webkit_web_view_set_display_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+    test->waitUntilDisplayCaptureStateChangedTo(WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+
+    // A de-activated capture device cannot be re-activated.
+    webkit_web_view_set_display_capture_state(test->m_webView, WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE);
+    g_assert_cmpuint(webkit_web_view_get_display_capture_state(test->m_webView), ==, WEBKIT_MEDIA_CAPTURE_STATE_NONE);
+
+    webkit_settings_set_enable_media_stream(settings, enabled);
+    webkit_settings_set_enable_mock_capture_devices(settings, FALSE);
+    webkitSettingsSetMediaCaptureRequiresSecureConnection(settings, TRUE);
+}
 #endif // ENABLE(MEDIA_STREAM)
 
 #if ENABLE(POINTER_LOCK)
@@ -1343,6 +1503,7 @@
     UIClientTest::add("WebKitWebView", "usermedia-enumeratedevices-permission-check", testWebViewUserMediaEnumerateDevicesPermissionCheck);
     UIClientTest::add("WebKitWebView", "usermedia-permission-requests", testWebViewUserMediaPermissionRequests);
     UIClientTest::add("WebKitWebView", "audio-usermedia-permission-request", testWebViewAudioOnlyUserMediaPermissionRequests);
+    UIClientTest::add("WebKitWebView", "display-usermedia-permission-request", testWebViewDisplayUserMediaPermissionRequests);
 #endif
     // FIXME: Implement mouse click in WPE.
 #if PLATFORM(GTK)

Modified: trunk/Tools/flatpak/flatpakutils.py (280171 => 280172)


--- trunk/Tools/flatpak/flatpakutils.py	2021-07-22 09:00:27 UTC (rev 280171)
+++ trunk/Tools/flatpak/flatpakutils.py	2021-07-22 10:54:07 UTC (rev 280172)
@@ -801,8 +801,10 @@
             "JSC",
             "MESA",
             "LIBGL",
+            "PIPEWIRE",
             "RUST",
             "SCCACHE",
+            "SPA",
             "WAYLAND",
             "WEBKIT",
             "WEBKIT2",
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to