feat: add support for system picker in setDisplayMediaRequestHandler (#43680)
* tmp Co-authored-by: Samuel Attard <marshallofsound@electronjs.org> * feat: add support for system picker in setDisplayMediaRequestHandler Co-authored-by: Samuel Attard <marshallofsound@electronjs.org> * oops Co-authored-by: Samuel Attard <marshallofsound@electronjs.org> * Apply suggestions from code review Co-authored-by: Erick Zhao <erick@hotmail.ca> Co-authored-by: Samuel Attard <sam@electronjs.org> * stuff Co-authored-by: Samuel Attard <marshallofsound@electronjs.org> * well... Co-authored-by: Samuel Attard <marshallofsound@electronjs.org> * seems legit Co-authored-by: Samuel Attard <marshallofsound@electronjs.org> * chore: update patch to handle screenCapturer Co-authored-by: Keeley Hammond <khammond@slack-corp.com> * feat: modify API to use useSystemPicker Co-authored-by: Keeley Hammond <khammond@slack-corp.com> * fix: gate ScreenCaptureKitPicker to macos 15 or higher Co-authored-by: Keeley Hammond <khammond@slack-corp.com> * fix: don't use native picker with legacy media selection Co-authored-by: Keeley Hammond <khammond@slack-corp.com> * chore: code review, boolean set & docs update Co-authored-by: Keeley Hammond <khammond@slack-corp.com> * fix: add cancelCallback Co-authored-by: Keeley Hammond <khammond@slack-corp.com> * docs: clarify session & desktopCapturer docs Co-authored-by: Keeley Hammond <khammond@slack-corp.com> * chore: update patches --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Samuel Attard <marshallofsound@electronjs.org> Co-authored-by: Samuel Attard <sam@electronjs.org> Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
This commit is contained in:
		
					parent
					
						
							
								57aeb9dfc6
							
						
					
				
			
			
				commit
				
					
						2aa2611f76
					
				
			
		
					 17 changed files with 433 additions and 7 deletions
				
			
		| 
						 | 
					@ -20,7 +20,11 @@ app.whenReady().then(() => {
 | 
				
			||||||
      // Grant access to the first screen found.
 | 
					      // Grant access to the first screen found.
 | 
				
			||||||
      callback({ video: sources[0], audio: 'loopback' })
 | 
					      callback({ video: sources[0], audio: 'loopback' })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  })
 | 
					    // If true, use the system picker if available.
 | 
				
			||||||
 | 
					    // Note: this is currently experimental. If the system picker
 | 
				
			||||||
 | 
					    // is available, it will be used and the media request handler
 | 
				
			||||||
 | 
					    // will not be invoked.
 | 
				
			||||||
 | 
					  }, { useSystemPicker: true })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mainWindow.loadFile('index.html')
 | 
					  mainWindow.loadFile('index.html')
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -953,7 +953,7 @@ session.fromPartition('some-partition').setPermissionCheckHandler((webContents,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### `ses.setDisplayMediaRequestHandler(handler)`
 | 
					#### `ses.setDisplayMediaRequestHandler(handler[, opts])`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* `handler` Function | null
 | 
					* `handler` Function | null
 | 
				
			||||||
  * `request` Object
 | 
					  * `request` Object
 | 
				
			||||||
| 
						 | 
					@ -980,12 +980,18 @@ session.fromPartition('some-partition').setPermissionCheckHandler((webContents,
 | 
				
			||||||
         and this is set to `true`, then local playback of audio will not be muted (e.g. using `MediaRecorder`
 | 
					         and this is set to `true`, then local playback of audio will not be muted (e.g. using `MediaRecorder`
 | 
				
			||||||
         to record `WebFrameMain` with this flag set to `true` will allow audio to pass through to the speakers
 | 
					         to record `WebFrameMain` with this flag set to `true` will allow audio to pass through to the speakers
 | 
				
			||||||
         while recording). Default is `false`.
 | 
					         while recording). Default is `false`.
 | 
				
			||||||
 | 
					* `opts` Object (optional) _macOS_ _Experimental_
 | 
				
			||||||
 | 
					  * `useSystemPicker` Boolean - true if the available native system picker should be used. Default is `false`. _macOS_ _Experimental_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This handler will be called when web content requests access to display media
 | 
					This handler will be called when web content requests access to display media
 | 
				
			||||||
via the `navigator.mediaDevices.getDisplayMedia` API. Use the
 | 
					via the `navigator.mediaDevices.getDisplayMedia` API. Use the
 | 
				
			||||||
[desktopCapturer](desktop-capturer.md) API to choose which stream(s) to grant
 | 
					[desktopCapturer](desktop-capturer.md) API to choose which stream(s) to grant
 | 
				
			||||||
access to.
 | 
					access to.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`useSystemPicker` allows an application to use the system picker instead of providing a specific video source from `getSources`.
 | 
				
			||||||
 | 
					This option is experimental, and currently available for MacOS 15+ only. If the system picker is available and `useSystemPicker`
 | 
				
			||||||
 | 
					is set to `true`, the handler will not be invoked.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```js
 | 
					```js
 | 
				
			||||||
const { session, desktopCapturer } = require('electron')
 | 
					const { session, desktopCapturer } = require('electron')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -994,7 +1000,11 @@ session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
 | 
				
			||||||
    // Grant access to the first screen found.
 | 
					    // Grant access to the first screen found.
 | 
				
			||||||
    callback({ video: sources[0] })
 | 
					    callback({ video: sources[0] })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
})
 | 
					  // Use the system picker if available.
 | 
				
			||||||
 | 
					  // Note: this is currently experimental. If the system picker
 | 
				
			||||||
 | 
					  // is available, it will be used and the media request handler
 | 
				
			||||||
 | 
					  // will not be invoked.
 | 
				
			||||||
 | 
					}, { useSystemPicker: true })
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Passing a [WebFrameMain](web-frame-main.md) object as a video or audio stream
 | 
					Passing a [WebFrameMain](web-frame-main.md) object as a video or audio stream
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -270,6 +270,7 @@ filenames = {
 | 
				
			||||||
    "shell/browser/api/electron_api_debugger.h",
 | 
					    "shell/browser/api/electron_api_debugger.h",
 | 
				
			||||||
    "shell/browser/api/electron_api_desktop_capturer.cc",
 | 
					    "shell/browser/api/electron_api_desktop_capturer.cc",
 | 
				
			||||||
    "shell/browser/api/electron_api_desktop_capturer.h",
 | 
					    "shell/browser/api/electron_api_desktop_capturer.h",
 | 
				
			||||||
 | 
					    "shell/browser/api/electron_api_desktop_capturer_mac.mm",
 | 
				
			||||||
    "shell/browser/api/electron_api_dialog.cc",
 | 
					    "shell/browser/api/electron_api_dialog.cc",
 | 
				
			||||||
    "shell/browser/api/electron_api_download_item.cc",
 | 
					    "shell/browser/api/electron_api_download_item.cc",
 | 
				
			||||||
    "shell/browser/api/electron_api_download_item.h",
 | 
					    "shell/browser/api/electron_api_download_item.h",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import { BrowserWindow } from 'electron/main';
 | 
					import { BrowserWindow } from 'electron/main';
 | 
				
			||||||
const { createDesktopCapturer } = process._linkedBinding('electron_browser_desktop_capturer');
 | 
					const { createDesktopCapturer, isDisplayMediaSystemPickerAvailable } = process._linkedBinding('electron_browser_desktop_capturer');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
 | 
					const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,8 @@ function isValid (options: Electron.SourcesOptions) {
 | 
				
			||||||
  return Array.isArray(options?.types);
 | 
					  return Array.isArray(options?.types);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { isDisplayMediaSystemPickerAvailable };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function getSources (args: Electron.SourcesOptions) {
 | 
					export async function getSources (args: Electron.SourcesOptions) {
 | 
				
			||||||
  if (!isValid(args)) throw new Error('Invalid options');
 | 
					  if (!isValid(args)) throw new Error('Invalid options');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,37 @@
 | 
				
			||||||
import { fetchWithSession } from '@electron/internal/browser/api/net-fetch';
 | 
					import { fetchWithSession } from '@electron/internal/browser/api/net-fetch';
 | 
				
			||||||
import { net } from 'electron/main';
 | 
					import { net } from 'electron/main';
 | 
				
			||||||
const { fromPartition, fromPath, Session } = process._linkedBinding('electron_browser_session');
 | 
					const { fromPartition, fromPath, Session } = process._linkedBinding('electron_browser_session');
 | 
				
			||||||
 | 
					const { isDisplayMediaSystemPickerAvailable } = process._linkedBinding('electron_browser_desktop_capturer');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fake video source that activates the native system picker
 | 
				
			||||||
 | 
					// This is used to get around the need for a screen/window
 | 
				
			||||||
 | 
					// id in Chrome's desktopCapturer.
 | 
				
			||||||
 | 
					let fakeVideoSourceId = -1;
 | 
				
			||||||
 | 
					const systemPickerVideoSource = Object.create(null);
 | 
				
			||||||
 | 
					Object.defineProperty(systemPickerVideoSource, 'id', {
 | 
				
			||||||
 | 
					  get () {
 | 
				
			||||||
 | 
					    return `window:${fakeVideoSourceId--}:0`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					systemPickerVideoSource.name = '';
 | 
				
			||||||
 | 
					Object.freeze(systemPickerVideoSource);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) {
 | 
					Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) {
 | 
				
			||||||
  return fetchWithSession(input, init, this, net.request);
 | 
					  return fetchWithSession(input, init, this, net.request);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Session.prototype.setDisplayMediaRequestHandler = function (handler, opts) {
 | 
				
			||||||
 | 
					  if (!handler) return this._setDisplayMediaRequestHandler(handler, opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this._setDisplayMediaRequestHandler(async (req, callback) => {
 | 
				
			||||||
 | 
					    if (opts && opts.useSystemPicker && isDisplayMediaSystemPickerAvailable()) {
 | 
				
			||||||
 | 
					      return callback({ video: systemPickerVideoSource });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return handler(req, callback);
 | 
				
			||||||
 | 
					  }, opts);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  fromPartition,
 | 
					  fromPartition,
 | 
				
			||||||
  fromPath,
 | 
					  fromPath,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,3 +129,4 @@ feat_enable_passing_exit_code_on_service_process_crash.patch
 | 
				
			||||||
chore_remove_reference_to_chrome_browser_themes.patch
 | 
					chore_remove_reference_to_chrome_browser_themes.patch
 | 
				
			||||||
feat_enable_customizing_symbol_color_in_framecaptionbutton.patch
 | 
					feat_enable_customizing_symbol_color_in_framecaptionbutton.patch
 | 
				
			||||||
build_expose_webplugininfo_interface_to_electron.patch
 | 
					build_expose_webplugininfo_interface_to_electron.patch
 | 
				
			||||||
 | 
					feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -199,7 +199,7 @@ index 58985ce62dc569256bad5e94de9c0d125fc470d0..33436784b691c860d58f8b4dfcc6718e
 | 
				
			||||||
           &SelectFileDialogLinuxKde::OnSelectSingleFolderDialogResponse, this,
 | 
					           &SelectFileDialogLinuxKde::OnSelectSingleFolderDialogResponse, this,
 | 
				
			||||||
           parent));
 | 
					           parent));
 | 
				
			||||||
diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.cc b/ui/shell_dialogs/select_file_dialog_linux_portal.cc
 | 
					diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.cc b/ui/shell_dialogs/select_file_dialog_linux_portal.cc
 | 
				
			||||||
index 61ddcbf7bf57e423099c7d392a19b3ec79b5d03f..b2d2e11f72dcca5b3791a6dd3e9e5ae930a1f701 100644
 | 
					index 61ddcbf7bf57e423099c7d392a19b3ec79b5d03f..920d0610943091f850e44e3e0481abd7fe08f881 100644
 | 
				
			||||||
--- a/ui/shell_dialogs/select_file_dialog_linux_portal.cc
 | 
					--- a/ui/shell_dialogs/select_file_dialog_linux_portal.cc
 | 
				
			||||||
+++ b/ui/shell_dialogs/select_file_dialog_linux_portal.cc
 | 
					+++ b/ui/shell_dialogs/select_file_dialog_linux_portal.cc
 | 
				
			||||||
@@ -44,7 +44,9 @@ constexpr char kMethodStartServiceByName[] = "StartServiceByName";
 | 
					@@ -44,7 +44,9 @@ constexpr char kMethodStartServiceByName[] = "StartServiceByName";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,331 @@
 | 
				
			||||||
 | 
					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 88c56f4dfcc1f8517ef1e8b6f1d37f5ba4d0b2c7..a75493a6d4d8ce8340a2d820eff5eed4e6a95109 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 8a774911ce0f610b2c993976d108f840696c1d02..5ead7287e2d765d043f8b9c0229a2ee825d9f544 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 8ac12480f663a74dfbdcf7128a582a81b4474d25..db6802a2603e1d3c3039e49737438124bf2ee1f1 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 b6129282c6807702cf88e0a3e2ba233e41a20960..1c2d0c6dd4101fe0bac69e3018bbbedadce224cc 100644
 | 
				
			||||||
 | 
					--- a/content/browser/media/capture/screen_capture_kit_device_mac.mm
 | 
				
			||||||
 | 
					+++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm
 | 
				
			||||||
 | 
					@@ -24,24 +24,83 @@
 | 
				
			||||||
 | 
					                                                     std::optional<gfx::Size>,
 | 
				
			||||||
 | 
					                                                     std::optional<gfx::Rect>)>;
 | 
				
			||||||
 | 
					 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
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 API_AVAILABLE(macos(12.3))
 | 
				
			||||||
 | 
					 @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;
 | 
				
			||||||
 | 
					@@ -141,7 +200,8 @@ + (SCStreamConfiguration*)streamConfigurationWithFrameSize:(gfx::Size)frameSize
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 class API_AVAILABLE(macos(12.3)) ScreenCaptureKitDeviceMac
 | 
				
			||||||
 | 
					     : public IOSurfaceCaptureDeviceBase,
 | 
				
			||||||
 | 
					-      public ScreenCaptureKitResetStreamInterface {
 | 
				
			||||||
 | 
					+      public ScreenCaptureKitResetStreamInterface
 | 
				
			||||||
 | 
					+       {
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					   explicit ScreenCaptureKitDeviceMac(const DesktopMediaID& source,
 | 
				
			||||||
 | 
					                                      SCContentFilter* filter)
 | 
				
			||||||
 | 
					@@ -152,18 +212,41 @@ explicit ScreenCaptureKitDeviceMac(const DesktopMediaID& source,
 | 
				
			||||||
 | 
					         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 (@available(macOS 15.0, *)) {
 | 
				
			||||||
 | 
					+      auto picker_callback = base::BindPostTask(
 | 
				
			||||||
 | 
					+        device_task_runner_,
 | 
				
			||||||
 | 
					+        base::BindRepeating(&ScreenCaptureKitDeviceMac::OnContentFilterReady, weak_factory_.GetWeakPtr())
 | 
				
			||||||
 | 
					+      );
 | 
				
			||||||
 | 
					+      auto* picker_observer = [[ScreenCaptureKitPickerHelper alloc] initWithStreamPickCallback:picker_callback cancelCallback:cancel_callback errorCallback:error_callback];
 | 
				
			||||||
 | 
					+      [[SCContentSharingPicker sharedPicker] addObserver:picker_observer];
 | 
				
			||||||
 | 
					+    }
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					   ScreenCaptureKitDeviceMac(const ScreenCaptureKitDeviceMac&) = delete;
 | 
				
			||||||
 | 
					   ScreenCaptureKitDeviceMac& operator=(const ScreenCaptureKitDeviceMac&) =
 | 
				
			||||||
 | 
					       delete;
 | 
				
			||||||
 | 
					-  ~ScreenCaptureKitDeviceMac() override = default;
 | 
				
			||||||
 | 
					+  ~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;
 | 
				
			||||||
 | 
					+      }
 | 
				
			||||||
 | 
					+    }
 | 
				
			||||||
 | 
					+  }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   void OnShareableContentCreated(SCShareableContent* content) {
 | 
				
			||||||
 | 
					     DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
 | 
				
			||||||
 | 
					@@ -232,7 +315,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.
 | 
				
			||||||
 | 
					@@ -241,6 +324,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;
 | 
				
			||||||
 | 
					@@ -254,6 +340,7 @@ void CreateStream(SCContentFilter* filter) {
 | 
				
			||||||
 | 
					     stream_ = [[SCStream alloc] initWithFilter:filter
 | 
				
			||||||
 | 
					                                  configuration:config
 | 
				
			||||||
 | 
					                                       delegate:helper_];
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					     {
 | 
				
			||||||
 | 
					       NSError* error = nil;
 | 
				
			||||||
 | 
					       bool add_stream_output_result =
 | 
				
			||||||
 | 
					@@ -395,7 +482,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");
 | 
				
			||||||
 | 
					@@ -418,23 +505,39 @@ void OnUpdateConfigurationError() {
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   // 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 < 0 && 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());
 | 
				
			||||||
 | 
					@@ -492,6 +595,8 @@ void ResetStreamTo(SCWindow* window) override {
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					+  static int active_streams_;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					   const DesktopMediaID source_;
 | 
				
			||||||
 | 
					   SCContentFilter* const filter_;
 | 
				
			||||||
 | 
					   const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
 | 
				
			||||||
 | 
					@@ -521,6 +626,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 7adf8264cfa9980c4a8414bf0f8bfa9ad70ec0b3..d162612dc70a2b57190aaf558aca8f46cbdedcad 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
 | 
				
			||||||
 | 
					@@ -360,13 +360,15 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
 | 
				
			||||||
 | 
					           std::move(after_start_capture_callback));
 | 
				
			||||||
 | 
					       break;
 | 
				
			||||||
 | 
					 #else
 | 
				
			||||||
 | 
					+      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/media/capture/video_capture_types.h b/media/capture/video_capture_types.h
 | 
				
			||||||
 | 
					index f2b75f5b2f547ad135c1288bf3639b26dedc8053..ef18724d9f2ea68a47b66fc3981f58a73ac1b51d 100644
 | 
				
			||||||
 | 
					--- a/media/capture/video_capture_types.h
 | 
				
			||||||
 | 
					+++ b/media/capture/video_capture_types.h
 | 
				
			||||||
 | 
					@@ -355,6 +355,8 @@ struct CAPTURE_EXPORT VideoCaptureParams {
 | 
				
			||||||
 | 
					   // Flag indicating whether HiDPI mode should be enabled for tab capture
 | 
				
			||||||
 | 
					   // sessions.
 | 
				
			||||||
 | 
					   bool is_high_dpi_enabled = true;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  std::optional<bool> use_native_picker;
 | 
				
			||||||
 | 
					 };
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 CAPTURE_EXPORT std::ostream& operator<<(
 | 
				
			||||||
| 
						 | 
					@ -503,6 +503,13 @@ gin::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) {
 | 
				
			||||||
  return handle;
 | 
					  return handle;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// static
 | 
				
			||||||
 | 
					#if !BUILDFLAG(IS_MAC)
 | 
				
			||||||
 | 
					bool DesktopCapturer::IsDisplayMediaSystemPickerAvailable() {
 | 
				
			||||||
 | 
					  return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gin::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder(
 | 
					gin::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder(
 | 
				
			||||||
    v8::Isolate* isolate) {
 | 
					    v8::Isolate* isolate) {
 | 
				
			||||||
  return gin::Wrappable<DesktopCapturer>::GetObjectTemplateBuilder(isolate)
 | 
					  return gin::Wrappable<DesktopCapturer>::GetObjectTemplateBuilder(isolate)
 | 
				
			||||||
| 
						 | 
					@ -524,6 +531,9 @@ void Initialize(v8::Local<v8::Object> exports,
 | 
				
			||||||
  gin_helper::Dictionary dict(context->GetIsolate(), exports);
 | 
					  gin_helper::Dictionary dict(context->GetIsolate(), exports);
 | 
				
			||||||
  dict.SetMethod("createDesktopCapturer",
 | 
					  dict.SetMethod("createDesktopCapturer",
 | 
				
			||||||
                 &electron::api::DesktopCapturer::Create);
 | 
					                 &electron::api::DesktopCapturer::Create);
 | 
				
			||||||
 | 
					  dict.SetMethod(
 | 
				
			||||||
 | 
					      "isDisplayMediaSystemPickerAvailable",
 | 
				
			||||||
 | 
					      &electron::api::DesktopCapturer::IsDisplayMediaSystemPickerAvailable);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace
 | 
					}  // namespace
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,6 +36,8 @@ class DesktopCapturer final : public gin::Wrappable<DesktopCapturer>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static gin::Handle<DesktopCapturer> Create(v8::Isolate* isolate);
 | 
					  static gin::Handle<DesktopCapturer> Create(v8::Isolate* isolate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static bool IsDisplayMediaSystemPickerAvailable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void StartHandling(bool capture_window,
 | 
					  void StartHandling(bool capture_window,
 | 
				
			||||||
                     bool capture_screen,
 | 
					                     bool capture_screen,
 | 
				
			||||||
                     const gfx::Size& thumbnail_size,
 | 
					                     const gfx::Size& thumbnail_size,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								shell/browser/api/electron_api_desktop_capturer_mac.mm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								shell/browser/api/electron_api_desktop_capturer_mac.mm
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					// Copyright (c) 2024 Salesforce, Inc.
 | 
				
			||||||
 | 
					// Use of this source code is governed by the MIT license that can be
 | 
				
			||||||
 | 
					// found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "shell/browser/api/electron_api_desktop_capturer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace electron::api {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// static
 | 
				
			||||||
 | 
					bool DesktopCapturer::IsDisplayMediaSystemPickerAvailable() {
 | 
				
			||||||
 | 
					  if (@available(macOS 15.0, *)) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace electron::api
 | 
				
			||||||
| 
						 | 
					@ -1607,7 +1607,7 @@ void Session::FillObjectTemplate(v8::Isolate* isolate,
 | 
				
			||||||
                 &Session::SetPermissionRequestHandler)
 | 
					                 &Session::SetPermissionRequestHandler)
 | 
				
			||||||
      .SetMethod("setPermissionCheckHandler",
 | 
					      .SetMethod("setPermissionCheckHandler",
 | 
				
			||||||
                 &Session::SetPermissionCheckHandler)
 | 
					                 &Session::SetPermissionCheckHandler)
 | 
				
			||||||
      .SetMethod("setDisplayMediaRequestHandler",
 | 
					      .SetMethod("_setDisplayMediaRequestHandler",
 | 
				
			||||||
                 &Session::SetDisplayMediaRequestHandler)
 | 
					                 &Session::SetDisplayMediaRequestHandler)
 | 
				
			||||||
      .SetMethod("setDevicePermissionHandler",
 | 
					      .SetMethod("setDevicePermissionHandler",
 | 
				
			||||||
                 &Session::SetDevicePermissionHandler)
 | 
					                 &Session::SetDevicePermissionHandler)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,6 +58,11 @@ void InitializeFeatureList() {
 | 
				
			||||||
  if (platform_specific_enable_features.size() > 0) {
 | 
					  if (platform_specific_enable_features.size() > 0) {
 | 
				
			||||||
    enable_features += std::string(",") + platform_specific_enable_features;
 | 
					    enable_features += std::string(",") + platform_specific_enable_features;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  std::string platform_specific_disable_features =
 | 
				
			||||||
 | 
					      DisablePlatformSpecificFeatures();
 | 
				
			||||||
 | 
					  if (platform_specific_disable_features.size() > 0) {
 | 
				
			||||||
 | 
					    disable_features += std::string(",") + platform_specific_disable_features;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  base::FeatureList::InitInstance(enable_features, disable_features);
 | 
					  base::FeatureList::InitInstance(enable_features, disable_features);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,6 +78,9 @@ void InitializeFieldTrials() {
 | 
				
			||||||
std::string EnablePlatformSpecificFeatures() {
 | 
					std::string EnablePlatformSpecificFeatures() {
 | 
				
			||||||
  return "";
 | 
					  return "";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					std::string DisablePlatformSpecificFeatures() {
 | 
				
			||||||
 | 
					  return "";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace electron
 | 
					}  // namespace electron
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@ namespace electron {
 | 
				
			||||||
void InitializeFeatureList();
 | 
					void InitializeFeatureList();
 | 
				
			||||||
void InitializeFieldTrials();
 | 
					void InitializeFieldTrials();
 | 
				
			||||||
std::string EnablePlatformSpecificFeatures();
 | 
					std::string EnablePlatformSpecificFeatures();
 | 
				
			||||||
 | 
					std::string DisablePlatformSpecificFeatures();
 | 
				
			||||||
}  // namespace electron
 | 
					}  // namespace electron
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif  // ELECTRON_SHELL_BROWSER_FEATURE_LIST_H_
 | 
					#endif  // ELECTRON_SHELL_BROWSER_FEATURE_LIST_H_
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,4 +31,13 @@ std::string EnablePlatformSpecificFeatures() {
 | 
				
			||||||
  return "";
 | 
					  return "";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string DisablePlatformSpecificFeatures() {
 | 
				
			||||||
 | 
					  if (@available(macOS 14.4, *)) {
 | 
				
			||||||
 | 
					    // Required to stop timing out getDisplayMedia while waiting for
 | 
				
			||||||
 | 
					    // the user to select a window with the picker
 | 
				
			||||||
 | 
					    return "TimeoutHangingVideoCaptureStarts";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return "";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace electron
 | 
					}  // namespace electron
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								typings/internal-ambient.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								typings/internal-ambient.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -213,7 +213,7 @@ declare namespace NodeJS {
 | 
				
			||||||
    _linkedBinding(name: 'electron_browser_app'): { app: Electron.App, App: Function };
 | 
					    _linkedBinding(name: 'electron_browser_app'): { app: Electron.App, App: Function };
 | 
				
			||||||
    _linkedBinding(name: 'electron_browser_auto_updater'): { autoUpdater: Electron.AutoUpdater };
 | 
					    _linkedBinding(name: 'electron_browser_auto_updater'): { autoUpdater: Electron.AutoUpdater };
 | 
				
			||||||
    _linkedBinding(name: 'electron_browser_crash_reporter'): CrashReporterBinding;
 | 
					    _linkedBinding(name: 'electron_browser_crash_reporter'): CrashReporterBinding;
 | 
				
			||||||
    _linkedBinding(name: 'electron_browser_desktop_capturer'): { createDesktopCapturer(): ElectronInternal.DesktopCapturer; };
 | 
					    _linkedBinding(name: 'electron_browser_desktop_capturer'): { createDesktopCapturer(): ElectronInternal.DesktopCapturer; isDisplayMediaSystemPickerAvailable(): boolean; };
 | 
				
			||||||
    _linkedBinding(name: 'electron_browser_event_emitter'): { setEventEmitterPrototype(prototype: Object): void; };
 | 
					    _linkedBinding(name: 'electron_browser_event_emitter'): { setEventEmitterPrototype(prototype: Object): void; };
 | 
				
			||||||
    _linkedBinding(name: 'electron_browser_global_shortcut'): { globalShortcut: Electron.GlobalShortcut };
 | 
					    _linkedBinding(name: 'electron_browser_global_shortcut'): { globalShortcut: Electron.GlobalShortcut };
 | 
				
			||||||
    _linkedBinding(name: 'electron_browser_image_view'): { ImageView: any };
 | 
					    _linkedBinding(name: 'electron_browser_image_view'): { ImageView: any };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								typings/internal-electron.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								typings/internal-electron.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -127,6 +127,10 @@ declare namespace Electron {
 | 
				
			||||||
    type?: 'backgroundPage' | 'window' | 'browserView' | 'remote' | 'webview' | 'offscreen';
 | 
					    type?: 'backgroundPage' | 'window' | 'browserView' | 'remote' | 'webview' | 'offscreen';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  interface Session {
 | 
				
			||||||
 | 
					    _setDisplayMediaRequestHandler: Electron.Session['setDisplayMediaRequestHandler'];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  type CreateWindowFunction = (options: BrowserWindowConstructorOptions) => WebContents;
 | 
					  type CreateWindowFunction = (options: BrowserWindowConstructorOptions) => WebContents;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  interface Menu {
 | 
					  interface Menu {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue