diff --git a/atom/browser/api/atom_api_system_preferences.cc b/atom/browser/api/atom_api_system_preferences.cc index b8c665456ade..2b11aad25278 100644 --- a/atom/browser/api/atom_api_system_preferences.cc +++ b/atom/browser/api/atom_api_system_preferences.cc @@ -5,6 +5,7 @@ #include "atom/browser/api/atom_api_system_preferences.h" #include "atom/common/native_mate_converters/callback.h" +#include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/node_includes.h" #include "native_mate/dictionary.h" diff --git a/atom/browser/api/atom_api_system_preferences.h b/atom/browser/api/atom_api_system_preferences.h index fed1c52247b7..7779ce007816 100644 --- a/atom/browser/api/atom_api_system_preferences.h +++ b/atom/browser/api/atom_api_system_preferences.h @@ -11,6 +11,10 @@ #include "base/callback.h" #include "native_mate/handle.h" +namespace base { +class DictionaryValue; +} + namespace atom { namespace api { @@ -22,11 +26,16 @@ class SystemPreferences : public mate::EventEmitter { static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); +#if defined(OS_MACOSX) + using NotificationCallback = base::Callback< + void(const std::string&, const base::DictionaryValue&)>; +#endif + #if defined(OS_WIN) bool IsAeroGlassEnabled(); #elif defined(OS_MACOSX) int SubscribeNotification(const std::string& name, - const base::Closure& callback); + const NotificationCallback& callback); void UnsubscribeNotification(int id); v8::Local GetUserDefault(const std::string& name, const std::string& type); diff --git a/atom/browser/api/atom_api_system_preferences_mac.mm b/atom/browser/api/atom_api_system_preferences_mac.mm index 2d12b278ae92..1bc65a14f94b 100644 --- a/atom/browser/api/atom_api_system_preferences_mac.mm +++ b/atom/browser/api/atom_api_system_preferences_mac.mm @@ -8,8 +8,10 @@ #import +#include "atom/browser/mac/dict_util.h" #include "atom/common/native_mate_converters/gurl_converter.h" #include "base/strings/sys_string_conversions.h" +#include "base/values.h" #include "net/base/mac/url_conversions.h" namespace atom { @@ -25,16 +27,26 @@ std::map g_id_map; } // namespace -int SystemPreferences::SubscribeNotification(const std::string& name, - const base::Closure& callback) { +int SystemPreferences::SubscribeNotification( + const std::string& name, const NotificationCallback& callback) { int request_id = g_next_id++; - __block base::Closure copied_callback = callback; + __block NotificationCallback copied_callback = callback; g_id_map[request_id] = [[NSDistributedNotificationCenter defaultCenter] addObserverForName:base::SysUTF8ToNSString(name) object:nil queue:nil usingBlock:^(NSNotification* notification) { - copied_callback.Run(); + scoped_ptr user_info = + NSDictionaryToDictionaryValue(notification.userInfo); + if (user_info) { + copied_callback.Run( + base::SysNSStringToUTF8(notification.name), + *user_info); + } else { + copied_callback.Run( + base::SysNSStringToUTF8(notification.name), + base::DictionaryValue()); + } } ]; return request_id; diff --git a/atom/browser/mac/dict_util.mm b/atom/browser/mac/dict_util.mm index 6a98e4cfdaad..1fb8401a2a23 100644 --- a/atom/browser/mac/dict_util.mm +++ b/atom/browser/mac/dict_util.mm @@ -4,13 +4,55 @@ #include "atom/browser/mac/dict_util.h" -#include "base/mac/scoped_nsobject.h" -#include "base/json/json_reader.h" #include "base/json/json_writer.h" +#include "base/strings/sys_string_conversions.h" #include "base/values.h" namespace atom { +namespace { + +scoped_ptr NSArrayToListValue(NSArray* arr) { + if (!arr) + return nullptr; + + scoped_ptr result(new base::ListValue); + for (id value in arr) { + if ([value isKindOfClass:[NSString class]]) { + result->AppendString(base::SysNSStringToUTF8(value)); + } else if ([value isKindOfClass:[NSNumber class]]) { + const char* objc_type = [value objCType]; + if (strcmp(objc_type, @encode(BOOL)) == 0 || + strcmp(objc_type, @encode(char)) == 0) + result->AppendBoolean([value boolValue]); + else if (strcmp(objc_type, @encode(double)) == 0 || + strcmp(objc_type, @encode(float)) == 0) + result->AppendDouble([value doubleValue]); + else + result->AppendInteger([value intValue]); + } else if ([value isKindOfClass:[NSArray class]]) { + scoped_ptr sub_arr = NSArrayToListValue(value); + if (sub_arr) + result->Append(std::move(sub_arr)); + else + result->Append(base::Value::CreateNullValue()); + } else if ([value isKindOfClass:[NSDictionary class]]) { + scoped_ptr sub_dict = + NSDictionaryToDictionaryValue(value); + if (sub_dict) + result->Append(std::move(sub_dict)); + else + result->Append(base::Value::CreateNullValue()); + } else { + result->AppendString(base::SysNSStringToUTF8([value description])); + } + } + + return result; +} + +} // namespace + NSDictionary* DictionaryValueToNSDictionary(const base::DictionaryValue& value) { std::string json; if (!base::JSONWriter::Write(value, &json)) @@ -26,17 +68,51 @@ NSDictionary* DictionaryValueToNSDictionary(const base::DictionaryValue& value) scoped_ptr NSDictionaryToDictionaryValue( NSDictionary* dict) { - NSData* data = [NSJSONSerialization dataWithJSONObject:dict - options:0 - error:nil]; - if (!data) + if (!dict) return nullptr; - base::scoped_nsobject json = - [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - scoped_ptr value = - base::JSONReader::Read([json UTF8String]); - return base::DictionaryValue::From(std::move(value)); + scoped_ptr result(new base::DictionaryValue); + for (id key in dict) { + std::string str_key = base::SysNSStringToUTF8( + [key isKindOfClass:[NSString class]] ? key : [key description]); + + id value = [dict objectForKey:key]; + if ([value isKindOfClass:[NSString class]]) { + result->SetStringWithoutPathExpansion( + str_key, base::SysNSStringToUTF8(value)); + } else if ([value isKindOfClass:[NSNumber class]]) { + const char* objc_type = [value objCType]; + if (strcmp(objc_type, @encode(BOOL)) == 0 || + strcmp(objc_type, @encode(char)) == 0) + result->SetBooleanWithoutPathExpansion(str_key, [value boolValue]); + else if (strcmp(objc_type, @encode(double)) == 0 || + strcmp(objc_type, @encode(float)) == 0) + result->SetDoubleWithoutPathExpansion(str_key, [value doubleValue]); + else + result->SetIntegerWithoutPathExpansion(str_key, [value intValue]); + } else if ([value isKindOfClass:[NSArray class]]) { + scoped_ptr sub_arr = NSArrayToListValue(value); + if (sub_arr) + result->SetWithoutPathExpansion(str_key, std::move(sub_arr)); + else + result->SetWithoutPathExpansion(str_key, + base::Value::CreateNullValue()); + } else if ([value isKindOfClass:[NSDictionary class]]) { + scoped_ptr sub_dict = + NSDictionaryToDictionaryValue(value); + if (sub_dict) + result->SetWithoutPathExpansion(str_key, std::move(sub_dict)); + else + result->SetWithoutPathExpansion(str_key, + base::Value::CreateNullValue()); + } else { + result->SetStringWithoutPathExpansion( + str_key, + base::SysNSStringToUTF8([value description])); + } + } + + return result; } } // namespace atom diff --git a/docs/api/system-preferences.md b/docs/api/system-preferences.md index df7ef89e65c4..8f4dc8993852 100644 --- a/docs/api/system-preferences.md +++ b/docs/api/system-preferences.md @@ -13,12 +13,16 @@ This method returns `true` if the system is in Dark Mode, and `false` otherwise. * `event` String * `callback` Function -Subscribes to native notifications of OS X, `callback` will be called when the -corresponding `event` happens. The `id` of the subscriber is returned, which can -be used to unsubscribe the `event`. +Subscribes to native notifications of OS X, `callback` will be called with +`callback(event, userInfo)` when the corresponding `event` happens. The +`userInfo` is an Object that contains the user information dictionary sent +along with the notification. + +The `id` of the subscriber is returned, which can be used to unsubscribe the +`event`. Under the hood this API subscribes to `NSDistributedNotificationCenter`, -possible values of `event` are: +example values of `event` are: * `AppleInterfaceThemeChangedNotification` * `AppleAquaColorVariantChanged`