electron_api_system_preferences_mac.mm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. // Copyright (c) 2016 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/api/electron_api_system_preferences.h"
  5. #include <map>
  6. #include <memory>
  7. #include <string>
  8. #include <utility>
  9. #import <AVFoundation/AVFoundation.h>
  10. #import <Cocoa/Cocoa.h>
  11. #import <LocalAuthentication/LocalAuthentication.h>
  12. #import <Security/Security.h>
  13. #include "base/mac/scoped_cftyperef.h"
  14. #include "base/strings/stringprintf.h"
  15. #include "base/strings/sys_string_conversions.h"
  16. #include "base/task/sequenced_task_runner.h"
  17. #include "base/values.h"
  18. #include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
  19. #include "net/base/mac/url_conversions.h"
  20. #include "shell/browser/mac/dict_util.h"
  21. #include "shell/browser/mac/electron_application.h"
  22. #include "shell/common/color_util.h"
  23. #include "shell/common/gin_converters/gurl_converter.h"
  24. #include "shell/common/gin_converters/value_converter.h"
  25. #include "shell/common/node_includes.h"
  26. #include "shell/common/process_util.h"
  27. #include "skia/ext/skia_utils_mac.h"
  28. #include "ui/native_theme/native_theme.h"
  29. namespace gin {
  30. template <>
  31. struct Converter<NSAppearance*> {
  32. static bool FromV8(v8::Isolate* isolate,
  33. v8::Local<v8::Value> val,
  34. NSAppearance** out) {
  35. if (val->IsNull()) {
  36. *out = nil;
  37. return true;
  38. }
  39. std::string name;
  40. if (!gin::ConvertFromV8(isolate, val, &name)) {
  41. return false;
  42. }
  43. if (name == "light") {
  44. *out = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
  45. return true;
  46. } else if (name == "dark") {
  47. if (@available(macOS 10.14, *)) {
  48. *out = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
  49. } else {
  50. *out = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
  51. }
  52. return true;
  53. }
  54. return false;
  55. }
  56. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, NSAppearance* val) {
  57. if (val == nil) {
  58. return v8::Null(isolate);
  59. }
  60. if ([val.name isEqualToString:NSAppearanceNameAqua]) {
  61. return gin::ConvertToV8(isolate, "light");
  62. }
  63. if (@available(macOS 10.14, *)) {
  64. if ([val.name isEqualToString:NSAppearanceNameDarkAqua]) {
  65. return gin::ConvertToV8(isolate, "dark");
  66. }
  67. }
  68. return gin::ConvertToV8(isolate, "unknown");
  69. }
  70. };
  71. } // namespace gin
  72. namespace electron::api {
  73. namespace {
  74. int g_next_id = 0;
  75. // The map to convert |id| to |int|.
  76. std::map<int, id> g_id_map;
  77. AVMediaType ParseMediaType(const std::string& media_type) {
  78. if (media_type == "camera") {
  79. return AVMediaTypeVideo;
  80. } else if (media_type == "microphone") {
  81. return AVMediaTypeAudio;
  82. } else {
  83. return nil;
  84. }
  85. }
  86. std::string ConvertSystemPermission(
  87. system_media_permissions::SystemPermission value) {
  88. using SystemPermission = system_media_permissions::SystemPermission;
  89. switch (value) {
  90. case SystemPermission::kNotDetermined:
  91. return "not-determined";
  92. case SystemPermission::kRestricted:
  93. return "restricted";
  94. case SystemPermission::kDenied:
  95. return "denied";
  96. case SystemPermission::kAllowed:
  97. return "granted";
  98. default:
  99. return "unknown";
  100. }
  101. }
  102. NSNotificationCenter* GetNotificationCenter(NotificationCenterKind kind) {
  103. switch (kind) {
  104. case NotificationCenterKind::kNSDistributedNotificationCenter:
  105. return [NSDistributedNotificationCenter defaultCenter];
  106. case NotificationCenterKind::kNSNotificationCenter:
  107. return [NSNotificationCenter defaultCenter];
  108. case NotificationCenterKind::kNSWorkspaceNotificationCenter:
  109. return [[NSWorkspace sharedWorkspace] notificationCenter];
  110. default:
  111. return nil;
  112. }
  113. }
  114. } // namespace
  115. void SystemPreferences::PostNotification(const std::string& name,
  116. base::Value::Dict user_info,
  117. gin::Arguments* args) {
  118. bool immediate = false;
  119. args->GetNext(&immediate);
  120. NSDistributedNotificationCenter* center =
  121. [NSDistributedNotificationCenter defaultCenter];
  122. [center
  123. postNotificationName:base::SysUTF8ToNSString(name)
  124. object:nil
  125. userInfo:DictionaryValueToNSDictionary(std::move(user_info))
  126. deliverImmediately:immediate];
  127. }
  128. int SystemPreferences::SubscribeNotification(
  129. v8::Local<v8::Value> maybe_name,
  130. const NotificationCallback& callback) {
  131. return DoSubscribeNotification(
  132. maybe_name, callback,
  133. NotificationCenterKind::kNSDistributedNotificationCenter);
  134. }
  135. void SystemPreferences::UnsubscribeNotification(int request_id) {
  136. DoUnsubscribeNotification(
  137. request_id, NotificationCenterKind::kNSDistributedNotificationCenter);
  138. }
  139. void SystemPreferences::PostLocalNotification(const std::string& name,
  140. base::Value::Dict user_info) {
  141. NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  142. [center
  143. postNotificationName:base::SysUTF8ToNSString(name)
  144. object:nil
  145. userInfo:DictionaryValueToNSDictionary(std::move(user_info))];
  146. }
  147. int SystemPreferences::SubscribeLocalNotification(
  148. v8::Local<v8::Value> maybe_name,
  149. const NotificationCallback& callback) {
  150. return DoSubscribeNotification(maybe_name, callback,
  151. NotificationCenterKind::kNSNotificationCenter);
  152. }
  153. void SystemPreferences::UnsubscribeLocalNotification(int request_id) {
  154. DoUnsubscribeNotification(request_id,
  155. NotificationCenterKind::kNSNotificationCenter);
  156. }
  157. void SystemPreferences::PostWorkspaceNotification(const std::string& name,
  158. base::Value::Dict user_info) {
  159. NSNotificationCenter* center =
  160. [[NSWorkspace sharedWorkspace] notificationCenter];
  161. [center
  162. postNotificationName:base::SysUTF8ToNSString(name)
  163. object:nil
  164. userInfo:DictionaryValueToNSDictionary(std::move(user_info))];
  165. }
  166. int SystemPreferences::SubscribeWorkspaceNotification(
  167. v8::Local<v8::Value> maybe_name,
  168. const NotificationCallback& callback) {
  169. return DoSubscribeNotification(
  170. maybe_name, callback,
  171. NotificationCenterKind::kNSWorkspaceNotificationCenter);
  172. }
  173. void SystemPreferences::UnsubscribeWorkspaceNotification(int request_id) {
  174. DoUnsubscribeNotification(
  175. request_id, NotificationCenterKind::kNSWorkspaceNotificationCenter);
  176. }
  177. int SystemPreferences::DoSubscribeNotification(
  178. v8::Local<v8::Value> maybe_name,
  179. const NotificationCallback& callback,
  180. NotificationCenterKind kind) {
  181. int request_id = g_next_id++;
  182. __block NotificationCallback copied_callback = callback;
  183. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  184. std::string name_str;
  185. if (!(maybe_name->IsNull() ||
  186. gin::ConvertFromV8(isolate, maybe_name, &name_str))) {
  187. isolate->ThrowException(v8::Exception::Error(
  188. gin::StringToV8(isolate, "Must pass null or a string")));
  189. return -1;
  190. }
  191. auto* name = maybe_name->IsNull() ? nil : base::SysUTF8ToNSString(name_str);
  192. g_id_map[request_id] = [GetNotificationCenter(kind)
  193. addObserverForName:name
  194. object:nil
  195. queue:nil
  196. usingBlock:^(NSNotification* notification) {
  197. std::string object = "";
  198. if ([notification.object isKindOfClass:[NSString class]]) {
  199. object = base::SysNSStringToUTF8(notification.object);
  200. }
  201. if (notification.userInfo) {
  202. copied_callback.Run(
  203. base::SysNSStringToUTF8(notification.name),
  204. base::Value(NSDictionaryToValue(notification.userInfo)),
  205. object);
  206. } else {
  207. copied_callback.Run(
  208. base::SysNSStringToUTF8(notification.name),
  209. base::Value(base::Value::Dict()), object);
  210. }
  211. }];
  212. return request_id;
  213. }
  214. void SystemPreferences::DoUnsubscribeNotification(int request_id,
  215. NotificationCenterKind kind) {
  216. auto iter = g_id_map.find(request_id);
  217. if (iter != g_id_map.end()) {
  218. id observer = iter->second;
  219. [GetNotificationCenter(kind) removeObserver:observer];
  220. g_id_map.erase(iter);
  221. }
  222. }
  223. v8::Local<v8::Value> SystemPreferences::GetUserDefault(
  224. v8::Isolate* isolate,
  225. const std::string& name,
  226. const std::string& type) {
  227. NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  228. NSString* key = base::SysUTF8ToNSString(name);
  229. if (type == "string") {
  230. return gin::StringToV8(
  231. isolate, base::SysNSStringToUTF8([defaults stringForKey:key]));
  232. } else if (type == "boolean") {
  233. return v8::Boolean::New(isolate, [defaults boolForKey:key]);
  234. } else if (type == "float") {
  235. return v8::Number::New(isolate, [defaults floatForKey:key]);
  236. } else if (type == "integer") {
  237. return v8::Integer::New(isolate, [defaults integerForKey:key]);
  238. } else if (type == "double") {
  239. return v8::Number::New(isolate, [defaults doubleForKey:key]);
  240. } else if (type == "url") {
  241. return gin::ConvertToV8(isolate,
  242. net::GURLWithNSURL([defaults URLForKey:key]));
  243. } else if (type == "array") {
  244. return gin::ConvertToV8(
  245. isolate, base::Value(NSArrayToValue([defaults arrayForKey:key])));
  246. } else if (type == "dictionary") {
  247. return gin::ConvertToV8(
  248. isolate,
  249. base::Value(NSDictionaryToValue([defaults dictionaryForKey:key])));
  250. } else {
  251. return v8::Undefined(isolate);
  252. }
  253. }
  254. void SystemPreferences::RegisterDefaults(gin::Arguments* args) {
  255. base::Value::Dict dict_value;
  256. if (!args->GetNext(&dict_value)) {
  257. args->ThrowError();
  258. return;
  259. }
  260. @try {
  261. NSDictionary* dict = DictionaryValueToNSDictionary(std::move(dict_value));
  262. for (id key in dict) {
  263. id value = [dict objectForKey:key];
  264. if ([value isKindOfClass:[NSNull class]] || value == nil) {
  265. args->ThrowError();
  266. return;
  267. }
  268. }
  269. [[NSUserDefaults standardUserDefaults] registerDefaults:dict];
  270. } @catch (NSException* exception) {
  271. args->ThrowError();
  272. }
  273. }
  274. void SystemPreferences::SetUserDefault(const std::string& name,
  275. const std::string& type,
  276. gin::Arguments* args) {
  277. NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  278. NSString* key = base::SysUTF8ToNSString(name);
  279. if (type == "string") {
  280. std::string value;
  281. if (args->GetNext(&value)) {
  282. [defaults setObject:base::SysUTF8ToNSString(value) forKey:key];
  283. return;
  284. }
  285. } else if (type == "boolean") {
  286. bool value;
  287. v8::Local<v8::Value> next = args->PeekNext();
  288. if (!next.IsEmpty() && next->IsBoolean() && args->GetNext(&value)) {
  289. [defaults setBool:value forKey:key];
  290. return;
  291. }
  292. } else if (type == "float") {
  293. float value;
  294. if (args->GetNext(&value)) {
  295. [defaults setFloat:value forKey:key];
  296. return;
  297. }
  298. } else if (type == "integer") {
  299. int value;
  300. if (args->GetNext(&value)) {
  301. [defaults setInteger:value forKey:key];
  302. return;
  303. }
  304. } else if (type == "double") {
  305. double value;
  306. if (args->GetNext(&value)) {
  307. [defaults setDouble:value forKey:key];
  308. return;
  309. }
  310. } else if (type == "url") {
  311. GURL value;
  312. if (args->GetNext(&value)) {
  313. if (NSURL* url = net::NSURLWithGURL(value)) {
  314. [defaults setURL:url forKey:key];
  315. return;
  316. }
  317. }
  318. } else if (type == "array") {
  319. base::Value value;
  320. if (args->GetNext(&value) && value.is_list()) {
  321. if (NSArray* array = ListValueToNSArray(value.GetList())) {
  322. [defaults setObject:array forKey:key];
  323. return;
  324. }
  325. }
  326. } else if (type == "dictionary") {
  327. base::Value value;
  328. if (args->GetNext(&value) && value.is_dict()) {
  329. if (NSDictionary* dict = DictionaryValueToNSDictionary(value.GetDict())) {
  330. [defaults setObject:dict forKey:key];
  331. return;
  332. }
  333. }
  334. } else {
  335. gin_helper::ErrorThrower(args->isolate())
  336. .ThrowTypeError("Invalid type: " + type);
  337. return;
  338. }
  339. gin_helper::ErrorThrower(args->isolate())
  340. .ThrowTypeError("Unable to convert value to: " + type);
  341. }
  342. std::string SystemPreferences::GetAccentColor() {
  343. NSColor* sysColor = nil;
  344. if (@available(macOS 10.14, *))
  345. sysColor = [NSColor controlAccentColor];
  346. return ToRGBAHex(skia::NSSystemColorToSkColor(sysColor),
  347. false /* include_hash */);
  348. }
  349. std::string SystemPreferences::GetSystemColor(gin_helper::ErrorThrower thrower,
  350. const std::string& color) {
  351. NSColor* sysColor = nil;
  352. if (color == "blue") {
  353. sysColor = [NSColor systemBlueColor];
  354. } else if (color == "brown") {
  355. sysColor = [NSColor systemBrownColor];
  356. } else if (color == "gray") {
  357. sysColor = [NSColor systemGrayColor];
  358. } else if (color == "green") {
  359. sysColor = [NSColor systemGreenColor];
  360. } else if (color == "orange") {
  361. sysColor = [NSColor systemOrangeColor];
  362. } else if (color == "pink") {
  363. sysColor = [NSColor systemPinkColor];
  364. } else if (color == "purple") {
  365. sysColor = [NSColor systemPurpleColor];
  366. } else if (color == "red") {
  367. sysColor = [NSColor systemRedColor];
  368. } else if (color == "yellow") {
  369. sysColor = [NSColor systemYellowColor];
  370. } else {
  371. thrower.ThrowError("Unknown system color: " + color);
  372. return "";
  373. }
  374. return ToRGBAHex(skia::NSSystemColorToSkColor(sysColor));
  375. }
  376. bool SystemPreferences::CanPromptTouchID() {
  377. base::scoped_nsobject<LAContext> context([[LAContext alloc] init]);
  378. LAPolicy auth_policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics;
  379. if (@available(macOS 10.15, *))
  380. auth_policy = LAPolicyDeviceOwnerAuthenticationWithBiometricsOrWatch;
  381. if (![context canEvaluatePolicy:auth_policy error:nil])
  382. return false;
  383. if (@available(macOS 10.13.2, *))
  384. return [context biometryType] == LABiometryTypeTouchID;
  385. return true;
  386. }
  387. v8::Local<v8::Promise> SystemPreferences::PromptTouchID(
  388. v8::Isolate* isolate,
  389. const std::string& reason) {
  390. gin_helper::Promise<void> promise(isolate);
  391. v8::Local<v8::Promise> handle = promise.GetHandle();
  392. base::scoped_nsobject<LAContext> context([[LAContext alloc] init]);
  393. base::ScopedCFTypeRef<SecAccessControlRef> access_control =
  394. base::ScopedCFTypeRef<SecAccessControlRef>(
  395. SecAccessControlCreateWithFlags(
  396. kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
  397. kSecAccessControlPrivateKeyUsage | kSecAccessControlUserPresence,
  398. nullptr));
  399. scoped_refptr<base::SequencedTaskRunner> runner =
  400. base::SequencedTaskRunner::GetCurrentDefault();
  401. __block gin_helper::Promise<void> p = std::move(promise);
  402. [context
  403. evaluateAccessControl:access_control
  404. operation:LAAccessControlOperationUseKeySign
  405. localizedReason:[NSString stringWithUTF8String:reason.c_str()]
  406. reply:^(BOOL success, NSError* error) {
  407. // NOLINTBEGIN(bugprone-use-after-move)
  408. if (!success) {
  409. std::string err_msg = std::string(
  410. [error.localizedDescription UTF8String]);
  411. runner->PostTask(
  412. FROM_HERE,
  413. base::BindOnce(
  414. gin_helper::Promise<void>::RejectPromise,
  415. std::move(p), std::move(err_msg)));
  416. } else {
  417. runner->PostTask(
  418. FROM_HERE,
  419. base::BindOnce(
  420. gin_helper::Promise<void>::ResolvePromise,
  421. std::move(p)));
  422. }
  423. // NOLINTEND(bugprone-use-after-move)
  424. }];
  425. return handle;
  426. }
  427. // static
  428. bool SystemPreferences::IsTrustedAccessibilityClient(bool prompt) {
  429. NSDictionary* options = @{(id)kAXTrustedCheckOptionPrompt : @(prompt)};
  430. return AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
  431. }
  432. std::string SystemPreferences::GetColor(gin_helper::ErrorThrower thrower,
  433. const std::string& color) {
  434. NSColor* sysColor = nil;
  435. if (color == "alternate-selected-control-text") {
  436. sysColor = [NSColor alternateSelectedControlTextColor];
  437. EmitWarning(
  438. node::Environment::GetCurrent(thrower.isolate()),
  439. "'alternate-selected-control-text' is deprecated as an input to "
  440. "getColor. Use 'selected-content-background' instead.",
  441. "electron");
  442. } else if (color == "control-background") {
  443. sysColor = [NSColor controlBackgroundColor];
  444. } else if (color == "control") {
  445. sysColor = [NSColor controlColor];
  446. } else if (color == "control-text") {
  447. sysColor = [NSColor controlTextColor];
  448. } else if (color == "disabled-control-text") {
  449. sysColor = [NSColor disabledControlTextColor];
  450. } else if (color == "find-highlight") {
  451. if (@available(macOS 10.14, *))
  452. sysColor = [NSColor findHighlightColor];
  453. } else if (color == "grid") {
  454. sysColor = [NSColor gridColor];
  455. } else if (color == "header-text") {
  456. sysColor = [NSColor headerTextColor];
  457. } else if (color == "highlight") {
  458. sysColor = [NSColor highlightColor];
  459. } else if (color == "keyboard-focus-indicator") {
  460. sysColor = [NSColor keyboardFocusIndicatorColor];
  461. } else if (color == "label") {
  462. sysColor = [NSColor labelColor];
  463. } else if (color == "link") {
  464. sysColor = [NSColor linkColor];
  465. } else if (color == "placeholder-text") {
  466. sysColor = [NSColor placeholderTextColor];
  467. } else if (color == "quaternary-label") {
  468. sysColor = [NSColor quaternaryLabelColor];
  469. } else if (color == "scrubber-textured-background") {
  470. sysColor = [NSColor scrubberTexturedBackgroundColor];
  471. } else if (color == "secondary-label") {
  472. sysColor = [NSColor secondaryLabelColor];
  473. } else if (color == "selected-content-background") {
  474. if (@available(macOS 10.14, *))
  475. sysColor = [NSColor selectedContentBackgroundColor];
  476. } else if (color == "selected-control") {
  477. sysColor = [NSColor selectedControlColor];
  478. } else if (color == "selected-control-text") {
  479. sysColor = [NSColor selectedControlTextColor];
  480. } else if (color == "selected-menu-item-text") {
  481. sysColor = [NSColor selectedMenuItemTextColor];
  482. } else if (color == "selected-text-background") {
  483. sysColor = [NSColor selectedTextBackgroundColor];
  484. } else if (color == "selected-text") {
  485. sysColor = [NSColor selectedTextColor];
  486. } else if (color == "separator") {
  487. if (@available(macOS 10.14, *))
  488. sysColor = [NSColor separatorColor];
  489. } else if (color == "shadow") {
  490. sysColor = [NSColor shadowColor];
  491. } else if (color == "tertiary-label") {
  492. sysColor = [NSColor tertiaryLabelColor];
  493. } else if (color == "text-background") {
  494. sysColor = [NSColor textBackgroundColor];
  495. } else if (color == "text") {
  496. sysColor = [NSColor textColor];
  497. } else if (color == "under-page-background") {
  498. sysColor = [NSColor underPageBackgroundColor];
  499. } else if (color == "unemphasized-selected-content-background") {
  500. if (@available(macOS 10.14, *))
  501. sysColor = [NSColor unemphasizedSelectedContentBackgroundColor];
  502. } else if (color == "unemphasized-selected-text-background") {
  503. if (@available(macOS 10.14, *))
  504. sysColor = [NSColor unemphasizedSelectedTextBackgroundColor];
  505. } else if (color == "unemphasized-selected-text") {
  506. if (@available(macOS 10.14, *))
  507. sysColor = [NSColor unemphasizedSelectedTextColor];
  508. } else if (color == "window-background") {
  509. sysColor = [NSColor windowBackgroundColor];
  510. } else if (color == "window-frame-text") {
  511. sysColor = [NSColor windowFrameTextColor];
  512. } else {
  513. thrower.ThrowError("Unknown color: " + color);
  514. }
  515. if (sysColor)
  516. return ToRGBHex(skia::NSSystemColorToSkColor(sysColor));
  517. return "";
  518. }
  519. std::string SystemPreferences::GetMediaAccessStatus(
  520. gin_helper::ErrorThrower thrower,
  521. const std::string& media_type) {
  522. if (media_type == "camera") {
  523. return ConvertSystemPermission(
  524. system_media_permissions::CheckSystemVideoCapturePermission());
  525. } else if (media_type == "microphone") {
  526. return ConvertSystemPermission(
  527. system_media_permissions::CheckSystemAudioCapturePermission());
  528. } else if (media_type == "screen") {
  529. return ConvertSystemPermission(
  530. system_media_permissions::CheckSystemScreenCapturePermission());
  531. } else {
  532. thrower.ThrowError("Invalid media type");
  533. return std::string();
  534. }
  535. }
  536. v8::Local<v8::Promise> SystemPreferences::AskForMediaAccess(
  537. v8::Isolate* isolate,
  538. const std::string& media_type) {
  539. gin_helper::Promise<bool> promise(isolate);
  540. v8::Local<v8::Promise> handle = promise.GetHandle();
  541. if (auto type = ParseMediaType(media_type)) {
  542. if (@available(macOS 10.14, *)) {
  543. __block gin_helper::Promise<bool> p = std::move(promise);
  544. [AVCaptureDevice requestAccessForMediaType:type
  545. completionHandler:^(BOOL granted) {
  546. dispatch_async(dispatch_get_main_queue(), ^{
  547. p.Resolve(!!granted);
  548. });
  549. }];
  550. } else {
  551. // access always allowed pre-10.14 Mojave
  552. promise.Resolve(true);
  553. }
  554. } else {
  555. promise.RejectWithErrorMessage("Invalid media type");
  556. }
  557. return handle;
  558. }
  559. void SystemPreferences::RemoveUserDefault(const std::string& name) {
  560. NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  561. [defaults removeObjectForKey:base::SysUTF8ToNSString(name)];
  562. }
  563. bool SystemPreferences::IsSwipeTrackingFromScrollEventsEnabled() {
  564. return [NSEvent isSwipeTrackingFromScrollEventsEnabled];
  565. }
  566. v8::Local<v8::Value> SystemPreferences::GetEffectiveAppearance(
  567. v8::Isolate* isolate) {
  568. if (@available(macOS 10.14, *)) {
  569. return gin::ConvertToV8(
  570. isolate, [NSApplication sharedApplication].effectiveAppearance);
  571. }
  572. return v8::Null(isolate);
  573. }
  574. v8::Local<v8::Value> SystemPreferences::GetAppLevelAppearance(
  575. v8::Isolate* isolate) {
  576. if (@available(macOS 10.14, *)) {
  577. return gin::ConvertToV8(isolate,
  578. [NSApplication sharedApplication].appearance);
  579. }
  580. return v8::Null(isolate);
  581. }
  582. void SystemPreferences::SetAppLevelAppearance(gin::Arguments* args) {
  583. if (@available(macOS 10.14, *)) {
  584. NSAppearance* appearance;
  585. if (args->GetNext(&appearance)) {
  586. [[NSApplication sharedApplication] setAppearance:appearance];
  587. } else {
  588. args->ThrowError();
  589. }
  590. }
  591. }
  592. } // namespace electron::api