From 8c471b4422afb7293c39dc86de01fec86abe2de3 Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Wed, 24 Jun 2020 02:34:51 +0200 Subject: [PATCH] feat: implement systemPreferences.getMediaAccessStatus() on Windows --- docs/api/system-preferences.md | 5 +- .../api/electron_api_system_preferences.cc | 4 +- .../api/electron_api_system_preferences.h | 4 +- .../electron_api_system_preferences_win.cc | 66 +++++++++++++++++++ spec-main/api-system-preferences-spec.ts | 2 +- 5 files changed, 75 insertions(+), 6 deletions(-) diff --git a/docs/api/system-preferences.md b/docs/api/system-preferences.md index 4d2261bae3dc3..a9c5ed5c5b4cd 100644 --- a/docs/api/system-preferences.md +++ b/docs/api/system-preferences.md @@ -416,7 +416,7 @@ This API itself will not protect your user data; rather, it is a mechanism to al Returns `Boolean` - `true` if the current process is a trusted accessibility client and `false` if it is not. -### `systemPreferences.getMediaAccessStatus(mediaType)` _macOS_ +### `systemPreferences.getMediaAccessStatus(mediaType)` _Windows_ _macOS_ * `mediaType` String - Can be `microphone`, `camera` or `screen`. @@ -426,6 +426,9 @@ This user consent was not required on macOS 10.13 High Sierra or lower so this m macOS 10.14 Mojave or higher requires consent for `microphone` and `camera` access. macOS 10.15 Catalina or higher requires consent for `screen` access. +Windows 10 has a global setting controlling `microphone` and `camera` access for all win32 applications. +It will always return `granted` for `screen` and for all media types on older versions of Windows. + ### `systemPreferences.askForMediaAccess(mediaType)` _macOS_ * `mediaType` String - the type of media being requested; can be `microphone`, `camera`. diff --git a/shell/browser/api/electron_api_system_preferences.cc b/shell/browser/api/electron_api_system_preferences.cc index 1067fe3c41d6c..db5260366b4f2 100644 --- a/shell/browser/api/electron_api_system_preferences.cc +++ b/shell/browser/api/electron_api_system_preferences.cc @@ -73,6 +73,8 @@ void SystemPreferences::BuildPrototype( #if defined(OS_WIN) || defined(OS_MACOSX) .SetMethod("getColor", &SystemPreferences::GetColor) .SetMethod("getAccentColor", &SystemPreferences::GetAccentColor) + .SetMethod("getMediaAccessStatus", + &SystemPreferences::GetMediaAccessStatus) #endif #if defined(OS_WIN) @@ -112,8 +114,6 @@ void SystemPreferences::BuildPrototype( .SetMethod("promptTouchID", &SystemPreferences::PromptTouchID) .SetMethod("isTrustedAccessibilityClient", &SystemPreferences::IsTrustedAccessibilityClient) - .SetMethod("getMediaAccessStatus", - &SystemPreferences::GetMediaAccessStatus) .SetMethod("askForMediaAccess", &SystemPreferences::AskForMediaAccess) #endif .SetMethod("isInvertedColorScheme", diff --git a/shell/browser/api/electron_api_system_preferences.h b/shell/browser/api/electron_api_system_preferences.h index e389082ebe0cc..3f9b0bcbede7f 100644 --- a/shell/browser/api/electron_api_system_preferences.h +++ b/shell/browser/api/electron_api_system_preferences.h @@ -49,6 +49,8 @@ class SystemPreferences : public gin_helper::EventEmitter std::string GetAccentColor(); std::string GetColor(gin_helper::ErrorThrower thrower, const std::string& color); + std::string GetMediaAccessStatus(const std::string& media_type, + gin_helper::Arguments* args); #endif #if defined(OS_WIN) bool IsAeroGlassEnabled(); @@ -99,8 +101,6 @@ class SystemPreferences : public gin_helper::EventEmitter static bool IsTrustedAccessibilityClient(bool prompt); - std::string GetMediaAccessStatus(const std::string& media_type, - gin_helper::Arguments* args); v8::Local AskForMediaAccess(v8::Isolate* isolate, const std::string& media_type); diff --git a/shell/browser/api/electron_api_system_preferences_win.cc b/shell/browser/api/electron_api_system_preferences_win.cc index 4ab67b9e46070..23f8f33fd239f 100644 --- a/shell/browser/api/electron_api_system_preferences_win.cc +++ b/shell/browser/api/electron_api_system_preferences_win.cc @@ -3,10 +3,13 @@ // found in the LICENSE file. #include +#include +#include #include #include "shell/browser/api/electron_api_system_preferences.h" +#include "base/win/core_winrt_util.h" #include "base/win/wrapped_window_proc.h" #include "shell/common/color_util.h" #include "ui/base/win/shell.h" @@ -20,6 +23,51 @@ namespace { const wchar_t kSystemPreferencesWindowClass[] = L"Electron_SystemPreferencesHostWindow"; +using ABI::Windows::Devices::Enumeration::DeviceAccessStatus; +using ABI::Windows::Devices::Enumeration::DeviceClass; +using ABI::Windows::Devices::Enumeration::IDeviceAccessInformation; +using ABI::Windows::Devices::Enumeration::IDeviceAccessInformationStatics; +using Microsoft::WRL::ComPtr; + +DeviceAccessStatus GetDeviceAccessStatus(DeviceClass device_class) { + ComPtr dev_access_info_statics; + HRESULT hr = base::win::GetActivationFactory< + IDeviceAccessInformationStatics, + RuntimeClass_Windows_Devices_Enumeration_DeviceAccessInformation>( + &dev_access_info_statics); + if (FAILED(hr)) { + VLOG(1) << "IDeviceAccessInformationStatics failed: " << hr; + return DeviceAccessStatus::DeviceAccessStatus_Allowed; + } + + ComPtr dev_access_info; + hr = dev_access_info_statics->CreateFromDeviceClass(device_class, + &dev_access_info); + if (FAILED(hr)) { + VLOG(1) << "IDeviceAccessInformation failed: " << hr; + return DeviceAccessStatus::DeviceAccessStatus_Allowed; + } + + auto status = DeviceAccessStatus::DeviceAccessStatus_Unspecified; + dev_access_info->get_CurrentStatus(&status); + return status; +} + +std::string ConvertDeviceAccessStatus(DeviceAccessStatus value) { + switch (value) { + case DeviceAccessStatus::DeviceAccessStatus_Unspecified: + return "not-determined"; + case DeviceAccessStatus::DeviceAccessStatus_Allowed: + return "granted"; + case DeviceAccessStatus::DeviceAccessStatus_DeniedBySystem: + return "restricted"; + case DeviceAccessStatus::DeviceAccessStatus_DeniedByUser: + return "denied"; + default: + return "unknown"; + } +} + } // namespace namespace api { @@ -117,6 +165,24 @@ std::string SystemPreferences::GetColor(gin_helper::ErrorThrower thrower, return ToRGBHex(color_utils::GetSysSkColor(id)); } +std::string SystemPreferences::GetMediaAccessStatus( + const std::string& media_type, + gin_helper::Arguments* args) { + if (media_type == "camera") { + return ConvertDeviceAccessStatus( + GetDeviceAccessStatus(DeviceClass::DeviceClass_VideoCapture)); + } else if (media_type == "microphone") { + return ConvertDeviceAccessStatus( + GetDeviceAccessStatus(DeviceClass::DeviceClass_AudioCapture)); + } else if (media_type == "screen") { + return ConvertDeviceAccessStatus( + DeviceAccessStatus::DeviceAccessStatus_Allowed); + } else { + args->ThrowError("Invalid media type"); + return std::string(); + } +} + void SystemPreferences::InitializeWindow() { invertered_color_scheme_ = IsInvertedColorScheme(); high_contrast_color_scheme_ = IsHighContrastColorScheme(); diff --git a/spec-main/api-system-preferences-spec.ts b/spec-main/api-system-preferences-spec.ts index b25127f06b9a1..ee3b5ae5c19cd 100644 --- a/spec-main/api-system-preferences-spec.ts +++ b/spec-main/api-system-preferences-spec.ts @@ -269,7 +269,7 @@ describe('systemPreferences module', () => { }); }); - ifdescribe(process.platform === 'darwin')('systemPreferences.getMediaAccessStatus(mediaType)', () => { + ifdescribe(['win32', 'darwin'].includes(process.platform))('systemPreferences.getMediaAccessStatus(mediaType)', () => { const statuses = ['not-determined', 'granted', 'denied', 'restricted', 'unknown']; it('returns an access status for a camera access request', () => {