Browse Source

feat: add APIs for accessing NSUserDefaults in the given domain

Milan Burda 5 years ago
parent
commit
2791af2a66

+ 32 - 4
docs/api/system-preferences.md

@@ -165,7 +165,7 @@ Add the specified defaults to your application's `NSUserDefaults`.
 * `type` String - Can be `string`, `boolean`, `integer`, `float`, `double`,
   `url`, `array` or `dictionary`.
 
-Returns `any` - The value of `key` in `NSUserDefaults`.
+Returns `any` - The value of `key` in `NSUserDefaults` for the app and current user.
 
 Some popular `key` and `type`s are:
 
@@ -177,13 +177,22 @@ Some popular `key` and `type`s are:
 * `NSPreferredWebServices`: `dictionary`
 * `NSUserDictionaryReplacementItems`: `array`
 
+### `systemPreferences.getUserDefaultInDomain(domain, key, type)` _macOS_
+
+* `domain` String
+* `key` String
+* `type` String - Can be `string`, `boolean`, `integer`, `float`, `double`,
+  `url`, `array` or `dictionary`.
+
+Returns `any` - The value of `key` in `NSUserDefaults` for the given `domain`.
+
 ### `systemPreferences.setUserDefault(key, type, value)` _macOS_
 
 * `key` String
 * `type` String - Can be `string`, `boolean`, `integer`, `float`, `double`, `url`, `array` or `dictionary`.
 * `value` String
 
-Set the value of `key` in `NSUserDefaults`.
+Set the value of `key` in `NSUserDefaults` for the app and current user.
 
 Note that `type` should match actual type of `value`. An exception is thrown
 if they don't.
@@ -192,12 +201,31 @@ Some popular `key` and `type`s are:
 
 * `ApplePressAndHoldEnabled`: `boolean`
 
+### `systemPreferences.setUserDefaultInDomain(domain, key, type, value)` _macOS_
+
+* `domain` String
+* `key` String
+* `type` String - See [`getUserDefault`](#systempreferencesgetuserdefaultkey-type-macos).
+* `value` String
+
+Set the value of `key` in `NSUserDefaults` for the given `domain`.
+
+Note that `type` should match actual type of `value`. An exception is thrown
+if they don't.
+
 ### `systemPreferences.removeUserDefault(key)` _macOS_
 
 * `key` String
 
-Removes the `key` in `NSUserDefaults`. This can be used to restore the default
-or global value of a `key` previously set with `setUserDefault`.
+Removes the `key` in `NSUserDefaults` for the app and current user.
+This can be used to restore the default or global value of a `key` previously set with `setUserDefault`.
+
+### `systemPreferences.removeUserDefaultInDomain(domain, key)` _macOS_
+
+* `domain` String
+* `key` String
+
+Removes the `key` in `NSUserDefaults` for the given `domain`.
 
 ### `systemPreferences.isAeroGlassEnabled()` _Windows_
 

+ 6 - 0
shell/browser/api/electron_api_system_preferences.cc

@@ -97,8 +97,14 @@ void SystemPreferences::BuildPrototype(
                  &SystemPreferences::UnsubscribeWorkspaceNotification)
       .SetMethod("registerDefaults", &SystemPreferences::RegisterDefaults)
       .SetMethod("getUserDefault", &SystemPreferences::GetUserDefault)
+      .SetMethod("getUserDefaultInDomain",
+                 &SystemPreferences::GetUserDefaultInDomain)
       .SetMethod("setUserDefault", &SystemPreferences::SetUserDefault)
+      .SetMethod("setUserDefaultInDomain",
+                 &SystemPreferences::SetUserDefaultInDomain)
       .SetMethod("removeUserDefault", &SystemPreferences::RemoveUserDefault)
+      .SetMethod("removeUserDefaultInDomain",
+                 &SystemPreferences::RemoveUserDefaultInDomain)
       .SetMethod("isSwipeTrackingFromScrollEventsEnabled",
                  &SystemPreferences::IsSwipeTrackingFromScrollEventsEnabled)
       .SetMethod("getEffectiveAppearance",

+ 12 - 3
shell/browser/api/electron_api_system_preferences.h

@@ -81,13 +81,22 @@ class SystemPreferences : public gin_helper::EventEmitter<SystemPreferences>
   int SubscribeWorkspaceNotification(const std::string& name,
                                      const NotificationCallback& callback);
   void UnsubscribeWorkspaceNotification(int request_id);
-  v8::Local<v8::Value> GetUserDefault(const std::string& name,
+  v8::Local<v8::Value> GetUserDefault(const std::string& key,
                                       const std::string& type);
+  v8::Local<v8::Value> GetUserDefaultInDomain(const std::string& domain,
+                                              const std::string& key,
+                                              const std::string& type);
   void RegisterDefaults(gin_helper::Arguments* args);
-  void SetUserDefault(const std::string& name,
+  void SetUserDefault(const std::string& key,
                       const std::string& type,
                       gin_helper::Arguments* args);
-  void RemoveUserDefault(const std::string& name);
+  void SetUserDefaultInDomain(const std::string& domain,
+                              const std::string& key,
+                              const std::string& type,
+                              gin_helper::Arguments* args);
+  void RemoveUserDefault(const std::string& key);
+  void RemoveUserDefaultInDomain(const std::string& domain,
+                                 const std::string& key);
   bool IsSwipeTrackingFromScrollEventsEnabled();
 
   std::string GetSystemColor(gin_helper::ErrorThrower thrower,

+ 127 - 106
shell/browser/api/electron_api_system_preferences_mac.mm

@@ -121,6 +121,103 @@ std::string ConvertSystemPermission(
   }
 }
 
+v8::Local<v8::Value> GetUserDefaultImpl(v8::Isolate* isolate,
+                                        NSUserDefaults* defaults,
+                                        const std::string& name,
+                                        const std::string& type) {
+  NSString* key = base::SysUTF8ToNSString(name);
+  if (type == "string") {
+    return gin::StringToV8(
+        isolate, base::SysNSStringToUTF8([defaults stringForKey:key]));
+  } else if (type == "boolean") {
+    return v8::Boolean::New(isolate, [defaults boolForKey:key]);
+  } else if (type == "float") {
+    return v8::Number::New(isolate, [defaults floatForKey:key]);
+  } else if (type == "integer") {
+    return v8::Integer::New(isolate, [defaults integerForKey:key]);
+  } else if (type == "double") {
+    return v8::Number::New(isolate, [defaults doubleForKey:key]);
+  } else if (type == "url") {
+    return gin::ConvertToV8(isolate,
+                            net::GURLWithNSURL([defaults URLForKey:key]));
+  } else if (type == "array") {
+    return gin::ConvertToV8(isolate,
+                            NSArrayToListValue([defaults arrayForKey:key]));
+  } else if (type == "dictionary") {
+    return gin::ConvertToV8(isolate, NSDictionaryToDictionaryValue(
+                                         [defaults dictionaryForKey:key]));
+  } else {
+    return v8::Undefined(isolate);
+  }
+}
+
+void SetUserDefaultImpl(NSUserDefaults* defaults,
+                        const std::string& name,
+                        const std::string& type,
+                        gin_helper::Arguments* args) {
+  NSString* key = base::SysUTF8ToNSString(name);
+  if (type == "string") {
+    std::string value;
+    if (args->GetNext(&value)) {
+      [defaults setObject:base::SysUTF8ToNSString(value) forKey:key];
+      return;
+    }
+  } else if (type == "boolean") {
+    bool value;
+    if (args->GetNext(&value)) {
+      [defaults setBool:value forKey:key];
+      return;
+    }
+  } else if (type == "float") {
+    float value;
+    if (args->GetNext(&value)) {
+      [defaults setFloat:value forKey:key];
+      return;
+    }
+  } else if (type == "integer") {
+    int value;
+    if (args->GetNext(&value)) {
+      [defaults setInteger:value forKey:key];
+      return;
+    }
+  } else if (type == "double") {
+    double value;
+    if (args->GetNext(&value)) {
+      [defaults setDouble:value forKey:key];
+      return;
+    }
+  } else if (type == "url") {
+    GURL value;
+    if (args->GetNext(&value)) {
+      if (NSURL* url = net::NSURLWithGURL(value)) {
+        [defaults setURL:url forKey:key];
+        return;
+      }
+    }
+  } else if (type == "array") {
+    base::ListValue value;
+    if (args->GetNext(&value)) {
+      if (NSArray* array = ListValueToNSArray(value)) {
+        [defaults setObject:array forKey:key];
+        return;
+      }
+    }
+  } else if (type == "dictionary") {
+    base::DictionaryValue value;
+    if (args->GetNext(&value)) {
+      if (NSDictionary* dict = DictionaryValueToNSDictionary(value)) {
+        [defaults setObject:dict forKey:key];
+        return;
+      }
+    }
+  } else {
+    args->ThrowError("Invalid type: " + type);
+    return;
+  }
+
+  args->ThrowError("Unable to convert value to: " + type);
+}
+
 }  // namespace
 
 void SystemPreferences::PostNotification(const std::string& name,
@@ -257,33 +354,19 @@ void SystemPreferences::DoUnsubscribeNotification(int request_id,
 }
 
 v8::Local<v8::Value> SystemPreferences::GetUserDefault(
-    const std::string& name,
+    const std::string& key,
     const std::string& type) {
   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
-  NSString* key = base::SysUTF8ToNSString(name);
-  if (type == "string") {
-    return gin::StringToV8(
-        isolate(), base::SysNSStringToUTF8([defaults stringForKey:key]));
-  } else if (type == "boolean") {
-    return v8::Boolean::New(isolate(), [defaults boolForKey:key]);
-  } else if (type == "float") {
-    return v8::Number::New(isolate(), [defaults floatForKey:key]);
-  } else if (type == "integer") {
-    return v8::Integer::New(isolate(), [defaults integerForKey:key]);
-  } else if (type == "double") {
-    return v8::Number::New(isolate(), [defaults doubleForKey:key]);
-  } else if (type == "url") {
-    return gin::ConvertToV8(isolate(),
-                            net::GURLWithNSURL([defaults URLForKey:key]));
-  } else if (type == "array") {
-    return gin::ConvertToV8(isolate(),
-                            NSArrayToListValue([defaults arrayForKey:key]));
-  } else if (type == "dictionary") {
-    return gin::ConvertToV8(isolate(), NSDictionaryToDictionaryValue(
-                                           [defaults dictionaryForKey:key]));
-  } else {
-    return v8::Undefined(isolate());
-  }
+  return GetUserDefaultImpl(isolate(), defaults, key, type);
+}
+
+v8::Local<v8::Value> SystemPreferences::GetUserDefaultInDomain(
+    const std::string& domain,
+    const std::string& key,
+    const std::string& type) {
+  base::scoped_nsobject<NSUserDefaults> defaults([[NSUserDefaults alloc]
+      initWithSuiteName:base::SysUTF8ToNSString(domain)]);
+  return GetUserDefaultImpl(isolate(), defaults, key, type);
 }
 
 void SystemPreferences::RegisterDefaults(gin_helper::Arguments* args) {
@@ -308,89 +391,20 @@ void SystemPreferences::RegisterDefaults(gin_helper::Arguments* args) {
   }
 }
 
-void SystemPreferences::SetUserDefault(const std::string& name,
+void SystemPreferences::SetUserDefault(const std::string& key,
                                        const std::string& type,
                                        gin_helper::Arguments* args) {
-  const auto throwConversionError = [&] {
-    args->ThrowError("Unable to convert value to: " + type);
-  };
-
   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
-  NSString* key = base::SysUTF8ToNSString(name);
-  if (type == "string") {
-    std::string value;
-    if (!args->GetNext(&value)) {
-      throwConversionError();
-      return;
-    }
-
-    [defaults setObject:base::SysUTF8ToNSString(value) forKey:key];
-  } else if (type == "boolean") {
-    bool value;
-    if (!args->GetNext(&value)) {
-      throwConversionError();
-      return;
-    }
-
-    [defaults setBool:value forKey:key];
-  } else if (type == "float") {
-    float value;
-    if (!args->GetNext(&value)) {
-      throwConversionError();
-      return;
-    }
-
-    [defaults setFloat:value forKey:key];
-  } else if (type == "integer") {
-    int value;
-    if (!args->GetNext(&value)) {
-      throwConversionError();
-      return;
-    }
-
-    [defaults setInteger:value forKey:key];
-  } else if (type == "double") {
-    double value;
-    if (!args->GetNext(&value)) {
-      throwConversionError();
-      return;
-    }
-
-    [defaults setDouble:value forKey:key];
-  } else if (type == "url") {
-    GURL value;
-    if (!args->GetNext(&value)) {
-      throwConversionError();
-      return;
-    }
-
-    if (NSURL* url = net::NSURLWithGURL(value)) {
-      [defaults setURL:url forKey:key];
-    }
-  } else if (type == "array") {
-    base::ListValue value;
-    if (!args->GetNext(&value)) {
-      throwConversionError();
-      return;
-    }
-
-    if (NSArray* array = ListValueToNSArray(value)) {
-      [defaults setObject:array forKey:key];
-    }
-  } else if (type == "dictionary") {
-    base::DictionaryValue value;
-    if (!args->GetNext(&value)) {
-      throwConversionError();
-      return;
-    }
+  SetUserDefaultImpl(defaults, key, type, args);
+}
 
-    if (NSDictionary* dict = DictionaryValueToNSDictionary(value)) {
-      [defaults setObject:dict forKey:key];
-    }
-  } else {
-    args->ThrowError("Invalid type: " + type);
-    return;
-  }
+void SystemPreferences::SetUserDefaultInDomain(const std::string& domain,
+                                               const std::string& key,
+                                               const std::string& type,
+                                               gin_helper::Arguments* args) {
+  base::scoped_nsobject<NSUserDefaults> defaults([[NSUserDefaults alloc]
+      initWithSuiteName:base::SysUTF8ToNSString(domain)]);
+  SetUserDefaultImpl(defaults, key, type, args);
 }
 
 std::string SystemPreferences::GetAccentColor() {
@@ -633,9 +647,16 @@ v8::Local<v8::Promise> SystemPreferences::AskForMediaAccess(
   return handle;
 }
 
-void SystemPreferences::RemoveUserDefault(const std::string& name) {
+void SystemPreferences::RemoveUserDefault(const std::string& key) {
   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
-  [defaults removeObjectForKey:base::SysUTF8ToNSString(name)];
+  [defaults removeObjectForKey:base::SysUTF8ToNSString(key)];
+}
+
+void SystemPreferences::RemoveUserDefaultInDomain(const std::string& domain,
+                                                  const std::string& key) {
+  base::scoped_nsobject<NSUserDefaults> defaults([[NSUserDefaults alloc]
+      initWithSuiteName:base::SysUTF8ToNSString(domain)]);
+  [defaults removeObjectForKey:base::SysUTF8ToNSString(key)];
 }
 
 bool SystemPreferences::IsDarkMode() {

+ 87 - 20
spec-main/api-system-preferences-spec.ts

@@ -68,30 +68,55 @@ describe('systemPreferences module', () => {
     });
 
     it('returns values for unknown user defaults', () => {
-      expect(systemPreferences.getUserDefault('UserDefaultDoesNotExist', 'boolean')).to.equal(false);
-      expect(systemPreferences.getUserDefault('UserDefaultDoesNotExist', 'integer')).to.equal(0);
-      expect(systemPreferences.getUserDefault('UserDefaultDoesNotExist', 'float')).to.equal(0);
-      expect(systemPreferences.getUserDefault('UserDefaultDoesNotExist', 'double')).to.equal(0);
-      expect(systemPreferences.getUserDefault('UserDefaultDoesNotExist', 'string')).to.equal('');
-      expect(systemPreferences.getUserDefault('UserDefaultDoesNotExist', 'url')).to.equal('');
-      expect(systemPreferences.getUserDefault('UserDefaultDoesNotExist', 'badtype' as any)).to.be.undefined('user default');
-      expect(systemPreferences.getUserDefault('UserDefaultDoesNotExist', 'array')).to.deep.equal([]);
-      expect(systemPreferences.getUserDefault('UserDefaultDoesNotExist', 'dictionary')).to.deep.equal({});
+      const KEY = 'UserDefaultDoesNotExist';
+
+      expect(systemPreferences.getUserDefault(KEY, 'boolean')).to.equal(false);
+      expect(systemPreferences.getUserDefault(KEY, 'integer')).to.equal(0);
+      expect(systemPreferences.getUserDefault(KEY, 'float')).to.equal(0);
+      expect(systemPreferences.getUserDefault(KEY, 'double')).to.equal(0);
+      expect(systemPreferences.getUserDefault(KEY, 'string')).to.equal('');
+      expect(systemPreferences.getUserDefault(KEY, 'url')).to.equal('');
+      expect(systemPreferences.getUserDefault(KEY, 'badtype' as any)).to.be.undefined('user default');
+      expect(systemPreferences.getUserDefault(KEY, 'array')).to.deep.equal([]);
+      expect(systemPreferences.getUserDefault(KEY, 'dictionary')).to.deep.equal({});
     });
   });
 
+  ifdescribe(process.platform === 'darwin')('systemPreferences.getUserDefaultInDomain(domain, key, type)', () => {
+    it('returns values for known user defaults', () => {
+      const value = systemPreferences.getUserDefaultInDomain('com.apple.notificationcenterui', 'doNotDisturb', 'boolean');
+      expect(value).to.be.a('boolean');
+    });
+
+    it('returns values for unknown user defaults', () => {
+      const DOMAIN = 'DomainDoesNotExist';
+      const KEY = 'UserDefaultDoesNotExist';
+
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'boolean')).to.equal(false);
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'integer')).to.equal(0);
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'float')).to.equal(0);
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'double')).to.equal(0);
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'string')).to.equal('');
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'url')).to.equal('');
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'badtype' as any)).to.be.undefined('user default');
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'array')).to.deep.equal([]);
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'dictionary')).to.deep.equal({});
+    });
+  });
+
+  const TEST_CASES = [
+    ['string', 'abc'],
+    ['boolean', true],
+    ['float', 2.5],
+    ['double', 10.1],
+    ['integer', 11],
+    ['url', 'https://github.com/electron'],
+    ['array', [1, 2, 3]],
+    ['dictionary', { 'a': 1, 'b': 2 }]
+  ];
+
   ifdescribe(process.platform === 'darwin')('systemPreferences.setUserDefault(key, type, value)', () => {
     const KEY = 'SystemPreferencesTest';
-    const TEST_CASES = [
-      ['string', 'abc'],
-      ['boolean', true],
-      ['float', 2.5],
-      ['double', 10.1],
-      ['integer', 11],
-      ['url', 'https://github.com/electron'],
-      ['array', [1, 2, 3]],
-      ['dictionary', { a: 1, b: 2 }]
-    ];
 
     it('sets values', () => {
       for (const [type, value] of TEST_CASES) {
@@ -116,6 +141,33 @@ describe('systemPreferences module', () => {
     });
   });
 
+  ifdescribe(process.platform === 'darwin')('systemPreferences.setUserDefaultInDomain(domain, key, type, value)', () => {
+    const DOMAIN = 'org.electronjs.spec';
+    const KEY = 'SystemPreferencesTest';
+
+    it('sets values', () => {
+      for (const [type, value] of TEST_CASES) {
+        systemPreferences.setUserDefaultInDomain(DOMAIN, KEY, type as any, value as any);
+        const retrievedValue = systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, type as any);
+        expect(retrievedValue).to.deep.equal(value);
+      }
+    });
+
+    it('throws when type and value conflict', () => {
+      for (const [type, value] of TEST_CASES) {
+        expect(() => {
+          systemPreferences.setUserDefaultInDomain(DOMAIN, KEY, type as any, typeof value === 'string' ? {} : 'foo' as any);
+        }).to.throw(`Unable to convert value to: ${type}`);
+      }
+    });
+
+    it('throws when type is not valid', () => {
+      expect(() => {
+        systemPreferences.setUserDefaultInDomain(DOMAIN, KEY, 'abc', 'foo');
+      }).to.throw('Invalid type: abc');
+    });
+  });
+
   ifdescribe(process.platform === 'darwin')('systemPreferences.getSystemColor(color)', () => {
     it('throws on invalid system colors', () => {
       const color = 'bad-color';
@@ -231,7 +283,7 @@ describe('systemPreferences module', () => {
     });
   });
 
-  ifdescribe(process.platform === 'darwin')('systemPreferences.setUserDefault(key, type, value)', () => {
+  ifdescribe(process.platform === 'darwin')('systemPreferences.removeUserDefault(key)', () => {
     it('removes keys', () => {
       const KEY = 'SystemPreferencesTest';
       systemPreferences.setUserDefault(KEY, 'string', 'foo');
@@ -244,6 +296,21 @@ describe('systemPreferences module', () => {
     });
   });
 
+  ifdescribe(process.platform === 'darwin')('systemPreferences.removeUserDefaultInDomain(domain, key)', () => {
+    const DOMAIN = 'org.electronjs.spec';
+
+    it('removes keys', () => {
+      const KEY = 'SystemPreferencesTest';
+      systemPreferences.setUserDefaultInDomain(DOMAIN, KEY, 'string', 'foo');
+      systemPreferences.removeUserDefaultInDomain(DOMAIN, KEY);
+      expect(systemPreferences.getUserDefaultInDomain(DOMAIN, KEY, 'string')).to.equal('');
+    });
+
+    it('does not throw for missing keys', () => {
+      systemPreferences.removeUserDefaultInDomain(DOMAIN, 'some-missing-key');
+    });
+  });
+
   describe('systemPreferences.isInvertedColorScheme()', () => {
     it('returns a boolean', () => {
       expect(systemPreferences.isInvertedColorScheme()).to.be.a('boolean');