Repository: electron/electron (language: cpp)

Issue:
[Bug]: desktopCapturer source IDs are valid for a single use on Wayland
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

27.0.0-beta.9

### What operating system are you using?

Other Linux

### Operating System Version

Fedora Workstation 39

### What arch are you using?

x64

### Last Known Working Electron version

_No response_

### Expected Behavior

1. Get a source ID from desktopCapturer
2. Display the stream using getUserMedia
3. Stop the stream
4. Display the stream again using getUserMedia

### Actual Behavior

XDG desktop portal permission dialog appears again

### Testcase Gist URL

https://gist.github.com/aiddya/cfd89d040661e0553425e4203ce4512b

### Additional Information

The fiddle has a timeout function that displays the stream after 10 seconds. So the steps to test using the fiddle boil down to: select a source, preview it, stop it and then wait 10 seconds.


Discussion / hints:

Fix patch:
diff --git a/patches/webrtc/.patches b/patches/webrtc/.patches
index 63f7e05ecd6b6..f6b71af744db7 100644
--- a/patches/webrtc/.patches
+++ b/patches/webrtc/.patches
@@ -1,3 +1,4 @@
 fix_fallback_to_x11_capturer_on_wayland.patch
 fix_mark_pipewire_capturer_as_failed_after_session_is_closed.patch
 fix_check_pipewire_init_before_creating_generic_capturer.patch
+pipewire_capturer_make_restore_tokens_re-usable_more_than_one_time.patch
diff --git a/patches/webrtc/pipewire_capturer_make_restore_tokens_re-usable_more_than_one_time.patch b/patches/webrtc/pipewire_capturer_make_restore_tokens_re-usable_more_than_one_time.patch
new file mode 100644
index 0000000000000..9efbb686287b9
--- /dev/null
+++ b/patches/webrtc/pipewire_capturer_make_restore_tokens_re-usable_more_than_one_time.patch
@@ -0,0 +1,73 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jan Grulich <grulja@gmail.com>
+Date: Mon, 9 Oct 2023 18:54:31 +0200
+Subject: PipeWire capturer: make restore tokens re-usable more than one time
+
+Do not automatically remove all tokens once we attempt to use them. This
+mitigates an issue with Google Meet where an additional instance of a
+DesktopCapturer is created and destroyed right away, taking away the
+token we would use otherwise. Also save the token under same SourceId
+once we get a new (but could be same) token from the restored session.
+
+Bug: webrtc:15544
+Change-Id: I565b22f5bf6a4d8a3b7d6d757f9c1046c7a0557d
+Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/322621
+Commit-Queue: Jan Grulich <grulja@gmail.com>
+Reviewed-by: Alexander Cooper <alcooper@chromium.org>
+Cr-Commit-Position: refs/heads/main@{#40892}
+
+diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
+index 5a67c18c1d1f62aa5e3162d9778ca665bac4a1bb..a5df76b0cdecd1e2e68f2f25c80c6b17de8bc808 100644
+--- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
++++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
+@@ -82,8 +82,10 @@ void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result,
+                       << static_cast<uint>(result);
+   } else if (ScreenCastPortal* screencast_portal = GetScreenCastPortal()) {
+     if (!screencast_portal->RestoreToken().empty()) {
++      const SourceId token_id =
++          selected_source_id_ ? selected_source_id_ : source_id_;
+       RestoreTokenManager::GetInstance().AddToken(
+-          source_id_, screencast_portal->RestoreToken());
++          token_id, screencast_portal->RestoreToken());
+     }
+   }
+ 
+@@ -138,7 +140,7 @@ void BaseCapturerPipeWire::Start(Callback* callback) {
+         ScreenCastPortal::PersistMode::kTransient);
+     if (selected_source_id_) {
+       screencast_portal->SetRestoreToken(
+-          RestoreTokenManager::GetInstance().TakeToken(selected_source_id_));
++          RestoreTokenManager::GetInstance().GetToken(selected_source_id_));
+     }
+   }
+ 
+diff --git a/modules/desktop_capture/linux/wayland/restore_token_manager.cc b/modules/desktop_capture/linux/wayland/restore_token_manager.cc
+index 5ca9b957a9e4f436bc09d4bc16019b169ae9ba9f..a17d9a49bb031efa340bfd61b4a6f8f5a86d09da 100644
+--- a/modules/desktop_capture/linux/wayland/restore_token_manager.cc
++++ b/modules/desktop_capture/linux/wayland/restore_token_manager.cc
+@@ -23,10 +23,8 @@ void RestoreTokenManager::AddToken(DesktopCapturer::SourceId id,
+   restore_tokens_.insert({id, token});
+ }
+ 
+-std::string RestoreTokenManager::TakeToken(DesktopCapturer::SourceId id) {
+-  std::string token = restore_tokens_[id];
+-  // Remove the token as it cannot be used anymore
+-  restore_tokens_.erase(id);
++std::string RestoreTokenManager::GetToken(DesktopCapturer::SourceId id) {
++  const std::string token = restore_tokens_[id];
+   return token;
+ }
+ 
+diff --git a/modules/desktop_capture/linux/wayland/restore_token_manager.h b/modules/desktop_capture/linux/wayland/restore_token_manager.h
+index 174bef121f74b7b2b529d681b86c4fb4218586ea..ad4f74790f2a5cfba304fc11d47c9924db9013d8 100644
+--- a/modules/desktop_capture/linux/wayland/restore_token_manager.h
++++ b/modules/desktop_capture/linux/wayland/restore_token_manager.h
+@@ -27,7 +27,7 @@ class RestoreTokenManager {
+   static RestoreTokenManager& GetInstance();
+ 
+   void AddToken(DesktopCapturer::SourceId id, const std::string& token);
+-  std::string TakeToken(DesktopCapturer::SourceId id);
++  std::string GetToken(DesktopCapturer::SourceId id);
+ 
+   // Returns a source ID which does not have any token associated with it yet.
+   DesktopCapturer::SourceId GetUnusedId();


Test patch: