From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <marshallofsound@electronjs.org>
Date: Thu, 8 Aug 2024 08:39:10 -0700
Subject: feat: allow usage of SCContentSharingPicker on supported platforms

This is implemented as a magic "window id" that instead of pulling an SCStream manually
instead farms out to the screen picker.

diff --git a/content/browser/media/capture/desktop_capture_device_mac.cc b/content/browser/media/capture/desktop_capture_device_mac.cc
index 120590a93bbc5a47e73c5d5515b7ad07b2364eb6..50a8b0dfe5400d1ab9da2893088583e4f815a140 100644
--- a/content/browser/media/capture/desktop_capture_device_mac.cc
+++ b/content/browser/media/capture/desktop_capture_device_mac.cc
@@ -28,7 +28,7 @@ class DesktopCaptureDeviceMac : public IOSurfaceCaptureDeviceBase {
   ~DesktopCaptureDeviceMac() override = default;
 
   // IOSurfaceCaptureDeviceBase:
-  void OnStart() override {
+  void OnStart(std::optional<bool> use_native_picker) override {
     requested_format_ = capture_params().requested_format;
     requested_format_.pixel_format = media::PIXEL_FORMAT_NV12;
     DCHECK_GT(requested_format_.frame_size.GetArea(), 0);
diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.cc b/content/browser/media/capture/io_surface_capture_device_base_mac.cc
index 7a76550922d97a835dae15be6a951c91a9b351b0..ce3e22e437fa208652326798e12952c40372197a 100644
--- a/content/browser/media/capture/io_surface_capture_device_base_mac.cc
+++ b/content/browser/media/capture/io_surface_capture_device_base_mac.cc
@@ -20,7 +20,7 @@ void IOSurfaceCaptureDeviceBase::AllocateAndStart(
   client_ = std::move(client);
   capture_params_ = params;
 
-  OnStart();
+  OnStart(params.use_native_picker);
 }
 
 void IOSurfaceCaptureDeviceBase::StopAndDeAllocate() {
diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.h b/content/browser/media/capture/io_surface_capture_device_base_mac.h
index e2771b7b281274cdcb601a5bc78a948ad592087b..48d116823a28213e50775f378e6ce04ce3af5072 100644
--- a/content/browser/media/capture/io_surface_capture_device_base_mac.h
+++ b/content/browser/media/capture/io_surface_capture_device_base_mac.h
@@ -25,7 +25,7 @@ class CONTENT_EXPORT IOSurfaceCaptureDeviceBase
   ~IOSurfaceCaptureDeviceBase() override;
 
   // OnStart is called by AllocateAndStart.
-  virtual void OnStart() = 0;
+  virtual void OnStart(std::optional<bool> use_native_picker) = 0;
 
   // OnStop is called by StopAndDeAllocate.
   virtual void OnStop() = 0;
diff --git a/content/browser/media/capture/screen_capture_kit_device_mac.mm b/content/browser/media/capture/screen_capture_kit_device_mac.mm
index 38aa41a82acdf42fb05d3315c4df408796437873..0c6cbbc920c65a6e671839eb8022fbc9ed697147 100644
--- a/content/browser/media/capture/screen_capture_kit_device_mac.mm
+++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm
@@ -28,6 +28,61 @@
                                                     std::optional<float>,
                                                     bool)>;
 using ErrorCallback = base::RepeatingClosure;
+using CancelCallback = base::RepeatingClosure;
+
+API_AVAILABLE(macos(15.0))
+@interface ScreenCaptureKitPickerHelper
+    : NSObject <SCContentSharingPickerObserver>
+
+- (void)contentSharingPicker:(SCContentSharingPicker *)picker
+          didCancelForStream:(SCStream *)stream;
+
+- (void)contentSharingPicker:(SCContentSharingPicker *)picker
+         didUpdateWithFilter:(SCContentFilter *)filter
+                   forStream:(SCStream *)stream;
+
+- (void)contentSharingPickerStartDidFailWithError:(NSError *)error;
+
+@end
+
+@implementation ScreenCaptureKitPickerHelper {
+  base::RepeatingCallback<void(SCContentFilter *)> _pickerCallback;
+  ErrorCallback _errorCallback;
+  CancelCallback _cancelCallback;
+}
+
+- (void)contentSharingPicker:(SCContentSharingPicker *)picker
+          didCancelForStream:(SCStream *)stream {
+  // TODO: This doesn't appear to be called on Apple's side;
+  // implement this logic
+  _cancelCallback.Run();
+}
+
+- (void)contentSharingPicker:(SCContentSharingPicker *)picker
+         didUpdateWithFilter:(SCContentFilter *)filter
+                   forStream:(SCStream *)stream {
+  if (stream == nil) {
+    _pickerCallback.Run(filter);
+    [picker removeObserver:self];
+  }
+}
+
+- (void)contentSharingPickerStartDidFailWithError:(NSError *)error {
+  _errorCallback.Run();
+}
+
+- (instancetype)initWithStreamPickCallback:(base::RepeatingCallback<void(SCContentFilter *)>)pickerCallback
+                             cancelCallback:(CancelCallback)cancelCallback
+                             errorCallback:(ErrorCallback)errorCallback {
+  if (self = [super init]) {
+    _pickerCallback = pickerCallback;
+    _cancelCallback = cancelCallback;
+    _errorCallback = errorCallback;
+  }
+  return self;
+}
+
+@end
 
 namespace {
 API_AVAILABLE(macos(12.3))
@@ -158,18 +213,22 @@ @interface ScreenCaptureKitDeviceHelper
     : NSObject <SCStreamDelegate, SCStreamOutput>
 
 - (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback
+                         cancelCallback:(CancelCallback)cancelCallback
                          errorCallback:(ErrorCallback)errorCallback;
 @end
 
 @implementation ScreenCaptureKitDeviceHelper {
   SampleCallback _sampleCallback;
+  CancelCallback _cancelCallback;
   ErrorCallback _errorCallback;
 }
 
 - (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback
+                         cancelCallback:(CancelCallback)cancelCallback
                          errorCallback:(ErrorCallback)errorCallback {
   if (self = [super init]) {
     _sampleCallback = sampleCallback;
+    _cancelCallback = cancelCallback;
     _errorCallback = errorCallback;
   }
   return self;
@@ -261,12 +320,11 @@ class API_AVAILABLE(macos(12.3)) ScreenCaptureKitDeviceMac
 
   explicit ScreenCaptureKitDeviceMac(
       const DesktopMediaID& source,
-      SCContentFilter* filter,
+      [[maybe_unused]] SCContentFilter* filter,
       StreamCallback stream_created_callback,
       std::unique_ptr<content::PipScreenCaptureCoordinatorProxy>
           pip_screen_capture_coordinator_proxy)
       : source_(source),
-        filter_(filter),
         stream_created_callback_(std::move(stream_created_callback)),
         device_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
         pip_screen_capture_coordinator_proxy_(
@@ -275,21 +333,43 @@ explicit ScreenCaptureKitDeviceMac(
         device_task_runner_,
         base::BindRepeating(&ScreenCaptureKitDeviceMac::OnStreamSample,
                             weak_factory_.GetWeakPtr()));
+    CancelCallback cancel_callback = base::BindPostTask(
+        device_task_runner_,
+        base::BindRepeating(&ScreenCaptureKitDeviceMac::OnStreamError,
+                            weak_factory_.GetWeakPtr()));
     ErrorCallback error_callback = base::BindPostTask(
         device_task_runner_,
         base::BindRepeating(&ScreenCaptureKitDeviceMac::OnStreamError,
                             weak_factory_.GetWeakPtr()));
     helper_ = [[ScreenCaptureKitDeviceHelper alloc]
         initWithSampleCallback:sample_callback
+                 cancelCallback:cancel_callback
                  errorCallback:error_callback];
     if (pip_screen_capture_coordinator_proxy_) {
       pip_screen_capture_coordinator_proxy_->AddObserver(this);
     }
+    if (@available(macOS 15.0, *)) {
+      auto picker_callback = base::BindPostTask(
+        device_task_runner_,
+        base::BindRepeating(&ScreenCaptureKitDeviceMac::OnContentFilterReady, weak_factory_.GetWeakPtr())
+      );
+      picker_helper_ = [[ScreenCaptureKitPickerHelper alloc] initWithStreamPickCallback:picker_callback cancelCallback:cancel_callback errorCallback:error_callback];
+      [[SCContentSharingPicker sharedPicker] addObserver:picker_helper_];
+    }
   }
   ScreenCaptureKitDeviceMac(const ScreenCaptureKitDeviceMac&) = delete;
   ScreenCaptureKitDeviceMac& operator=(const ScreenCaptureKitDeviceMac&) =
       delete;
   ~ScreenCaptureKitDeviceMac() override {
+    if (@available(macOS 15.0, *)) {
+      auto* picker = [SCContentSharingPicker sharedPicker];
+      ScreenCaptureKitDeviceMac::active_streams_--;
+      picker.maximumStreamCount = @(ScreenCaptureKitDeviceMac::active_streams_);
+      if (ScreenCaptureKitDeviceMac::active_streams_ == 0 && picker.active) {
+        picker.active = false;
+        [[SCContentSharingPicker sharedPicker] removeObserver:picker_helper_];
+      }
+    }
     DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
     if (pip_screen_capture_coordinator_proxy_) {
       pip_screen_capture_coordinator_proxy_->RemoveObserver(this);
@@ -380,7 +460,7 @@ void CreateStream(SCContentFilter* filter) {
       return;
     }
 
-    if (@available(macOS 14.0, *)) {
+    if (@available(macOS 15.0, *)) {
       // Update the content size. This step is neccessary when used together
       // with SCContentSharingPicker. If the Chrome picker is used, it will
       // change to retina resolution if applicable.
@@ -389,6 +469,9 @@ void CreateStream(SCContentFilter* filter) {
                     filter.contentRect.size.height * filter.pointPixelScale);
     }
 
+    OnContentFilterReady(filter);
+  }
+  void OnContentFilterReady(SCContentFilter* filter) {
     gfx::RectF dest_rect_in_frame;
     actual_capture_format_ = capture_params().requested_format;
     actual_capture_format_.pixel_format = media::PIXEL_FORMAT_NV12;
@@ -402,6 +485,7 @@ void CreateStream(SCContentFilter* filter) {
     stream_ = [[SCStream alloc] initWithFilter:filter
                                  configuration:config
                                       delegate:helper_];
+
     {
       NSError* error = nil;
       bool add_stream_output_result =
@@ -561,7 +645,7 @@ void OnStreamError() {
       if (fullscreen_module_) {
         fullscreen_module_->Reset();
       }
-      OnStart();
+      OnStart(std::nullopt);
     } else {
       client()->OnError(media::VideoCaptureError::kScreenCaptureKitStreamError,
                         FROM_HERE, "Stream delegate called didStopWithError");
@@ -647,23 +731,41 @@ void OnStateChanged(
   }
 
   // IOSurfaceCaptureDeviceBase:
-  void OnStart() override {
+  void OnStart(std::optional<bool> use_native_picker) override {
     DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
-    if (filter_) {
-      // SCContentSharingPicker is used where filter_ is set on creation.
-      CreateStream(filter_);
-    } else {
-      // Chrome picker is used.
-      auto content_callback = base::BindPostTask(
-          device_task_runner_,
-          base::BindRepeating(
-              &ScreenCaptureKitDeviceMac::OnShareableContentCreated,
-              weak_factory_.GetWeakPtr()));
-      auto handler = ^(SCShareableContent* content, NSError* error) {
-        content_callback.Run(content);
-      };
-      [SCShareableContent getShareableContentWithCompletionHandler:handler];
+
+    if (@available(macOS 15.0, *)) {
+      constexpr bool DefaultUseNativePicker = true;
+      if (use_native_picker.value_or(DefaultUseNativePicker) &&
+          source_.id == DesktopMediaID::kMacOsNativePickerId &&
+          source_.window_id < 0) {
+        auto* picker = [SCContentSharingPicker sharedPicker];
+        ScreenCaptureKitDeviceMac::active_streams_++;
+        picker.maximumStreamCount = @(ScreenCaptureKitDeviceMac::active_streams_);
+        if (!picker.active) {
+          picker.active = true;
+        }
+        NSMutableArray<NSNumber*>* exclude_ns_windows = [NSMutableArray array];
+        [[[[NSApplication sharedApplication] windows] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSWindow* win, NSDictionary *bindings) {
+          return [win sharingType] == NSWindowSharingNone;
+        }]] enumerateObjectsUsingBlock:^(NSWindow* win, NSUInteger idx, BOOL *stop) {
+          [exclude_ns_windows addObject:@([win windowNumber])];
+        }];
+        picker.defaultConfiguration.excludedWindowIDs = exclude_ns_windows;
+        [picker present];
+        return;
+      }
     }
+
+    auto content_callback = base::BindPostTask(
+        device_task_runner_,
+        base::BindRepeating(
+            &ScreenCaptureKitDeviceMac::OnShareableContentCreated,
+            weak_factory_.GetWeakPtr()));
+    auto handler = ^(SCShareableContent* content, NSError* error) {
+      content_callback.Run(content);
+    };
+    [SCShareableContent getShareableContentWithCompletionHandler:handler];
   }
   void OnStop() override {
     DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
@@ -722,7 +824,7 @@ void ResetStreamTo(SCWindow* window) override {
 
  private:
   const DesktopMediaID source_;
-  SCContentFilter* const filter_;
+  static int active_streams_;
   StreamCallback stream_created_callback_;
   const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
 
@@ -739,6 +841,10 @@ void ResetStreamTo(SCWindow* window) override {
   // Helper class that acts as output and delegate for `stream_`.
   ScreenCaptureKitDeviceHelper* __strong helper_;
 
+  // Helper class that acts as an observer for SCContentSharingPicker
+  API_AVAILABLE(macos(15.0))
+  ScreenCaptureKitPickerHelper* __strong picker_helper_;
+
   // This is used to detect when a captured presentation enters fullscreen mode.
   // If this happens, the module will call the ResetStreamTo function.
   std::unique_ptr<ScreenCaptureKitFullscreenModule> fullscreen_module_;
@@ -753,6 +859,8 @@ void ResetStreamTo(SCWindow* window) override {
   base::WeakPtrFactory<ScreenCaptureKitDeviceMac> weak_factory_{this};
 };
 
+int ScreenCaptureKitDeviceMac::active_streams_ = 0;
+
 }  // namespace
 
 // Although ScreenCaptureKit is available in 12.3 there were some bugs that
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
index 9887b022744234f11322806982962390d79031ac..631022789ffb51b45b120520977bea7239f28636 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
@@ -321,8 +321,16 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
         break;
       }
 
+#if defined(USE_AURA)
+      bool allow_window_id = false;
+#elif BUILDFLAG(IS_MAC)
+      bool allow_window_id =
+        desktop_id.id == DesktopMediaID::kMacOsNativePickerId;
+#endif
+
 #if defined(USE_AURA) || BUILDFLAG(IS_MAC)
-      if (desktop_id.window_id != DesktopMediaID::kNullId) {
+      if (!allow_window_id &&
+          desktop_id.window_id != DesktopMediaID::kNullId) {
         // For the other capturers, when a bug reports the type of capture it's
         // easy enough to determine which capturer was used, but it's a little
         // fuzzier with window capture.
@@ -338,13 +346,15 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
       }
 #endif  // defined(USE_AURA) || BUILDFLAG(IS_MAC)
 
+      media::VideoCaptureParams updated_params = params;
+      updated_params.use_native_picker = stream_type != blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE;
       // All cases other than tab capture or Aura desktop/window capture.
       TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
                            "UsingDesktopCapturer", TRACE_EVENT_SCOPE_THREAD);
       start_capture_closure = base::BindOnce(
           &InProcessVideoCaptureDeviceLauncher::
               DoStartDesktopCaptureOnDeviceThread,
-          base::Unretained(this), desktop_id, params,
+          base::Unretained(this), desktop_id, updated_params,
           CreateDeviceClient(media::VideoCaptureBufferType::kSharedMemory,
                              kMaxNumberOfBuffers, std::move(receiver),
                              std::move(receiver_on_io_thread)),
diff --git a/content/public/browser/desktop_media_id.h b/content/public/browser/desktop_media_id.h
index b10c5376caa9a832826868c72dbc44ee54705283..e9b27b53d9b34fbb0a3410eb2fcc15306497cdf4 100644
--- a/content/public/browser/desktop_media_id.h
+++ b/content/public/browser/desktop_media_id.h
@@ -28,6 +28,8 @@ struct CONTENT_EXPORT DesktopMediaID {
   static constexpr Id kNullId = 0;
   // Represents a fake id to create a dummy capturer for autotests.
   static constexpr Id kFakeId = -3;
+  // Represents an id to use native macOS picker for screenshare
+  static constexpr Id kMacOsNativePickerId = -4;
 
 #if defined(USE_AURA) || BUILDFLAG(IS_MAC)
   // Assigns integer identifier to the |window| and returns its DesktopMediaID.
diff --git a/media/capture/video_capture_types.h b/media/capture/video_capture_types.h
index f422e0ec81ee354512a10dafdd1bd8659a7e3d67..8095a5cb0060ed20a4b82e98b4921a8919077d7e 100644
--- a/media/capture/video_capture_types.h
+++ b/media/capture/video_capture_types.h
@@ -365,6 +365,8 @@ struct CAPTURE_EXPORT VideoCaptureParams {
   // of the capture is dynamically changed, as for example when using
   // share-this-tab-instead.
   uint32_t capture_version_source = 0;
+
+  std::optional<bool> use_native_picker;
 };
 
 CAPTURE_EXPORT std::ostream& operator<<(
