OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/notifications/notification_platform_bridge_mac.h" | 5 #include "chrome/browser/notifications/notification_platform_bridge_mac.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" |
| 11 #include "base/mac/bundle_locations.h" |
9 #include "base/mac/foundation_util.h" | 12 #include "base/mac/foundation_util.h" |
10 #include "base/mac/mac_util.h" | 13 #include "base/mac/mac_util.h" |
| 14 #include "base/mac/scoped_nsobject.h" |
11 #include "base/strings/string_number_conversions.h" | 15 #include "base/strings/string_number_conversions.h" |
12 #include "base/strings/sys_string_conversions.h" | 16 #include "base/strings/sys_string_conversions.h" |
13 #include "chrome/browser/browser_process.h" | 17 #include "chrome/browser/browser_process.h" |
14 #include "chrome/browser/notifications/native_notification_display_service.h" | 18 #include "chrome/browser/notifications/native_notification_display_service.h" |
15 #include "chrome/browser/notifications/notification.h" | 19 #include "chrome/browser/notifications/notification.h" |
16 #include "chrome/browser/notifications/notification_common.h" | 20 #include "chrome/browser/notifications/notification_common.h" |
17 #include "chrome/browser/notifications/notification_display_service_factory.h" | 21 #include "chrome/browser/notifications/notification_display_service_factory.h" |
18 #include "chrome/browser/notifications/persistent_notification_delegate.h" | 22 #include "chrome/browser/notifications/persistent_notification_delegate.h" |
19 #include "chrome/browser/notifications/platform_notification_service_impl.h" | 23 #include "chrome/browser/notifications/platform_notification_service_impl.h" |
20 #include "chrome/browser/profiles/profile.h" | 24 #include "chrome/browser/profiles/profile.h" |
21 #include "chrome/browser/profiles/profile_manager.h" | 25 #include "chrome/browser/profiles/profile_manager.h" |
22 #include "chrome/browser/ui/cocoa/notifications/notification_builder_mac.h" | 26 #include "chrome/browser/ui/cocoa/notifications/notification_builder_mac.h" |
| 27 #import "chrome/browser/ui/cocoa/notifications/notification_delivery.h" |
23 #include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h" | 28 #include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h" |
24 #import "chrome/browser/ui/cocoa/notifications/notification_response_builder_mac
.h" | 29 #import "chrome/browser/ui/cocoa/notifications/notification_response_builder_mac
.h" |
| 30 #include "chrome/common/features.h" |
| 31 #include "chrome/grit/generated_resources.h" |
25 #include "components/url_formatter/elide_url.h" | 32 #include "components/url_formatter/elide_url.h" |
26 #include "third_party/WebKit/public/platform/modules/notifications/WebNotificati
onConstants.h" | 33 #include "third_party/WebKit/public/platform/modules/notifications/WebNotificati
onConstants.h" |
| 34 #include "ui/base/l10n/l10n_util_mac.h" |
27 #include "url/gurl.h" | 35 #include "url/gurl.h" |
28 #include "url/origin.h" | 36 #include "url/origin.h" |
29 | 37 |
30 @class NSUserNotification; | 38 @class NSUserNotification; |
31 @class NSUserNotificationCenter; | 39 @class NSUserNotificationCenter; |
32 | 40 |
33 // The mapping from web notifications to NsUserNotification works as follows | 41 // The mapping from web notifications to NsUserNotification works as follows |
34 | 42 |
35 // notification#title in NSUserNotification.title | 43 // notification#title in NSUserNotification.title |
36 // notification#message in NSUserNotification.informativeText | 44 // notification#message in NSUserNotification.informativeText |
(...skipping 27 matching lines...) Expand all Loading... |
64 } | 72 } |
65 | 73 |
66 NotificationDisplayService* display_service = | 74 NotificationDisplayService* display_service = |
67 NotificationDisplayServiceFactory::GetForProfile(profile); | 75 NotificationDisplayServiceFactory::GetForProfile(profile); |
68 | 76 |
69 static_cast<NativeNotificationDisplayService*>(display_service) | 77 static_cast<NativeNotificationDisplayService*>(display_service) |
70 ->ProcessNotificationOperation(operation, notification_type, origin, | 78 ->ProcessNotificationOperation(operation, notification_type, origin, |
71 notification_id, action_index); | 79 notification_id, action_index); |
72 } | 80 } |
73 | 81 |
| 82 // Loads the profile and process the Notification response |
| 83 void DoProcessNotificationResponse(NotificationCommon::Operation operation, |
| 84 NotificationCommon::Type type, |
| 85 const std::string& profile_id, |
| 86 bool incognito, |
| 87 const std::string& origin, |
| 88 const std::string& notification_id, |
| 89 int32_t button_index) { |
| 90 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 91 ProfileManager* profileManager = g_browser_process->profile_manager(); |
| 92 DCHECK(profileManager); |
| 93 |
| 94 profileManager->LoadProfile( |
| 95 profile_id, incognito, base::Bind(&ProfileLoadedCallback, operation, type, |
| 96 origin, notification_id, button_index)); |
| 97 } |
| 98 |
74 } // namespace | 99 } // namespace |
75 | 100 |
76 // static | 101 // static |
77 NotificationPlatformBridge* NotificationPlatformBridge::Create() { | 102 NotificationPlatformBridge* NotificationPlatformBridge::Create() { |
78 return new NotificationPlatformBridgeMac( | 103 return new NotificationPlatformBridgeMac( |
79 [NSUserNotificationCenter defaultUserNotificationCenter]); | 104 [NSUserNotificationCenter defaultUserNotificationCenter]); |
80 } | 105 } |
81 | 106 |
82 // A Cocoa class that represents the delegate of NSUserNotificationCenter and | 107 // A Cocoa class that represents the delegate of NSUserNotificationCenter and |
83 // can forward commands to C++. | 108 // can forward commands to C++. |
84 @interface NotificationCenterDelegate | 109 @interface NotificationCenterDelegate |
85 : NSObject<NSUserNotificationCenterDelegate> { | 110 : NSObject<NSUserNotificationCenterDelegate> { |
86 } | 111 } |
87 @end | 112 @end |
88 | 113 |
| 114 // Interface to communicate with the Alert XPC service. |
| 115 @interface NotificationRemoteDispatcher : NSObject |
| 116 |
| 117 - (void)dispatchNotification:(NSDictionary*)data; |
| 118 |
| 119 @end |
| 120 |
89 // ///////////////////////////////////////////////////////////////////////////// | 121 // ///////////////////////////////////////////////////////////////////////////// |
90 | 122 |
91 NotificationPlatformBridgeMac::NotificationPlatformBridgeMac( | 123 NotificationPlatformBridgeMac::NotificationPlatformBridgeMac( |
92 NSUserNotificationCenter* notification_center) | 124 NSUserNotificationCenter* notification_center) |
93 : delegate_([NotificationCenterDelegate alloc]), | 125 : delegate_([NotificationCenterDelegate alloc]), |
94 notification_center_(notification_center) { | 126 notification_center_(notification_center), |
| 127 #if BUILDFLAG(ENABLE_XPC_NOTIFICATIONS) |
| 128 notification_remote_dispatcher_( |
| 129 [[NotificationRemoteDispatcher alloc] init]) |
| 130 #else |
| 131 notification_remote_dispatcher_(nullptr) |
| 132 #endif // ENABLE_XPC_NOTIFICATIONS |
| 133 { |
95 [notification_center_ setDelegate:delegate_.get()]; | 134 [notification_center_ setDelegate:delegate_.get()]; |
96 } | 135 } |
97 | 136 |
98 NotificationPlatformBridgeMac::~NotificationPlatformBridgeMac() { | 137 NotificationPlatformBridgeMac::~NotificationPlatformBridgeMac() { |
99 [notification_center_ setDelegate:nil]; | 138 [notification_center_ setDelegate:nil]; |
100 | 139 |
101 // TODO(miguelg) lift this restriction if possible. | 140 // TODO(miguelg) remove only alerts shown by the XPC service. |
| 141 // TODO(miguelg) do not remove banners if possible. |
102 [notification_center_ removeAllDeliveredNotifications]; | 142 [notification_center_ removeAllDeliveredNotifications]; |
103 } | 143 } |
104 | 144 |
105 void NotificationPlatformBridgeMac::Display( | 145 void NotificationPlatformBridgeMac::Display( |
106 NotificationCommon::Type notification_type, | 146 NotificationCommon::Type notification_type, |
107 const std::string& notification_id, | 147 const std::string& notification_id, |
108 const std::string& profile_id, | 148 const std::string& profile_id, |
109 bool incognito, | 149 bool incognito, |
110 const Notification& notification) { | 150 const Notification& notification) { |
111 base::scoped_nsobject<NotificationBuilder> builder( | 151 base::scoped_nsobject<NotificationBuilder> builder( |
(...skipping 20 matching lines...) Expand all Loading... |
132 NSString* buttonOne = SysUTF16ToNSString(buttons[0].title); | 172 NSString* buttonOne = SysUTF16ToNSString(buttons[0].title); |
133 NSString* buttonTwo = nullptr; | 173 NSString* buttonTwo = nullptr; |
134 if (buttons.size() > 1) | 174 if (buttons.size() > 1) |
135 buttonTwo = SysUTF16ToNSString(buttons[1].title); | 175 buttonTwo = SysUTF16ToNSString(buttons[1].title); |
136 [builder setButtons:buttonOne secondaryButton:buttonTwo]; | 176 [builder setButtons:buttonOne secondaryButton:buttonTwo]; |
137 } | 177 } |
138 | 178 |
139 // Tag | 179 // Tag |
140 if (!notification.tag().empty()) { | 180 if (!notification.tag().empty()) { |
141 [builder setTag:base::SysUTF8ToNSString(notification.tag())]; | 181 [builder setTag:base::SysUTF8ToNSString(notification.tag())]; |
| 182 |
142 // If renotify is needed, delete the notification with the same tag | 183 // If renotify is needed, delete the notification with the same tag |
143 // from the notification center before displaying this one. | 184 // from the notification center before displaying this one. |
144 // TODO(miguelg): This will need to work for alerts as well via XPC | 185 // TODO(miguelg): This will need to work for alerts as well via XPC |
145 // once supported. | 186 // once supported. |
146 if (notification.renotify()) { | 187 if (notification.renotify()) { |
147 NSUserNotificationCenter* notification_center = | 188 NSUserNotificationCenter* notification_center = |
148 [NSUserNotificationCenter defaultUserNotificationCenter]; | 189 [NSUserNotificationCenter defaultUserNotificationCenter]; |
149 for (NSUserNotification* existing_notification in | 190 for (NSUserNotification* existing_notification in |
150 [notification_center deliveredNotifications]) { | 191 [notification_center deliveredNotifications]) { |
151 NSString* identifier = | 192 NSString* identifier = |
152 [existing_notification valueForKey:@"identifier"]; | 193 [existing_notification valueForKey:@"identifier"]; |
153 if ([identifier | 194 if ([identifier |
154 isEqualToString:base::SysUTF8ToNSString(notification.tag())]) { | 195 isEqualToString:base::SysUTF8ToNSString(notification.tag())]) { |
155 [notification_center | 196 [notification_center |
156 removeDeliveredNotification:existing_notification]; | 197 removeDeliveredNotification:existing_notification]; |
157 break; | 198 break; |
158 } | 199 } |
159 } | 200 } |
160 } | 201 } |
161 } | 202 } |
162 | 203 |
163 [builder setOrigin:base::SysUTF8ToNSString(notification.origin_url().spec())]; | 204 [builder setOrigin:base::SysUTF8ToNSString(notification.origin_url().spec())]; |
164 [builder setNotificationId:base::SysUTF8ToNSString(notification_id)]; | 205 [builder setNotificationId:base::SysUTF8ToNSString(notification_id)]; |
165 [builder setProfileId:base::SysUTF8ToNSString(profile_id)]; | 206 [builder setProfileId:base::SysUTF8ToNSString(profile_id)]; |
166 [builder setIncognito:incognito]; | 207 [builder setIncognito:incognito]; |
167 [builder setNotificationType:[NSNumber numberWithInteger:notification_type]]; | 208 [builder setNotificationType:[NSNumber numberWithInteger:notification_type]]; |
168 | 209 |
| 210 #if BUILDFLAG(ENABLE_XPC_NOTIFICATIONS) |
| 211 // Send persistent notifications to the XPC service so they |
| 212 // can be displayed as alerts. Chrome itself can only display |
| 213 // banners. |
| 214 if (notification.never_timeout()) { |
| 215 NSDictionary* dict = [builder buildDictionary]; |
| 216 [notification_remote_dispatcher_ dispatchNotification:dict]; |
| 217 } else { |
| 218 NSUserNotification* toast = [builder buildUserNotification]; |
| 219 [notification_center_ deliverNotification:toast]; |
| 220 } |
| 221 #else |
169 NSUserNotification* toast = [builder buildUserNotification]; | 222 NSUserNotification* toast = [builder buildUserNotification]; |
170 [notification_center_ deliverNotification:toast]; | 223 [notification_center_ deliverNotification:toast]; |
| 224 #endif // ENABLE_XPC_NOTIFICATIONS |
171 } | 225 } |
172 | 226 |
173 void NotificationPlatformBridgeMac::Close(const std::string& profile_id, | 227 void NotificationPlatformBridgeMac::Close(const std::string& profile_id, |
174 const std::string& notification_id) { | 228 const std::string& notification_id) { |
175 NSString* candidate_id = base::SysUTF8ToNSString(notification_id); | 229 NSString* candidate_id = base::SysUTF8ToNSString(notification_id); |
176 | 230 |
177 NSString* current_profile_id = base::SysUTF8ToNSString(profile_id); | 231 NSString* current_profile_id = base::SysUTF8ToNSString(profile_id); |
178 for (NSUserNotification* toast in | 232 for (NSUserNotification* toast in |
179 [notification_center_ deliveredNotifications]) { | 233 [notification_center_ deliveredNotifications]) { |
180 NSString* toast_id = | 234 NSString* toast_id = |
(...skipping 26 matching lines...) Expand all Loading... |
207 } | 261 } |
208 } | 262 } |
209 return true; | 263 return true; |
210 } | 264 } |
211 | 265 |
212 bool NotificationPlatformBridgeMac::SupportsNotificationCenter() const { | 266 bool NotificationPlatformBridgeMac::SupportsNotificationCenter() const { |
213 return true; | 267 return true; |
214 } | 268 } |
215 | 269 |
216 // static | 270 // static |
| 271 void NotificationPlatformBridgeMac::ProcessNotificationResponse( |
| 272 NSDictionary* response) { |
| 273 if (!NotificationPlatformBridgeMac::VerifyNotificationData(response)) |
| 274 return; |
| 275 |
| 276 NSNumber* button_index = |
| 277 [response objectForKey:notification_constants::kNotificationButtonIndex]; |
| 278 NSNumber* operation = |
| 279 [response objectForKey:notification_constants::kNotificationOperation]; |
| 280 |
| 281 std::string notification_origin = base::SysNSStringToUTF8( |
| 282 [response objectForKey:notification_constants::kNotificationOrigin]); |
| 283 std::string notification_id = base::SysNSStringToUTF8( |
| 284 [response objectForKey:notification_constants::kNotificationId]); |
| 285 std::string profile_id = base::SysNSStringToUTF8( |
| 286 [response objectForKey:notification_constants::kNotificationProfileId]); |
| 287 NSNumber* is_incognito = |
| 288 [response objectForKey:notification_constants::kNotificationIncognito]; |
| 289 NSNumber* notification_type = |
| 290 [response objectForKey:notification_constants::kNotificationType]; |
| 291 |
| 292 content::BrowserThread::PostTask( |
| 293 content::BrowserThread::UI, FROM_HERE, |
| 294 base::Bind(DoProcessNotificationResponse, |
| 295 static_cast<NotificationCommon::Operation>( |
| 296 operation.unsignedIntValue), |
| 297 static_cast<NotificationCommon::Type>( |
| 298 notification_type.unsignedIntValue), |
| 299 profile_id, [is_incognito boolValue], notification_origin, |
| 300 notification_id, button_index.intValue)); |
| 301 } |
| 302 |
| 303 // static |
217 bool NotificationPlatformBridgeMac::VerifyNotificationData( | 304 bool NotificationPlatformBridgeMac::VerifyNotificationData( |
218 NSDictionary* response) { | 305 NSDictionary* response) { |
219 if (![response | 306 if (![response |
220 objectForKey:notification_constants::kNotificationButtonIndex] || | 307 objectForKey:notification_constants::kNotificationButtonIndex] || |
221 ![response objectForKey:notification_constants::kNotificationOperation] || | 308 ![response objectForKey:notification_constants::kNotificationOperation] || |
222 ![response objectForKey:notification_constants::kNotificationId] || | 309 ![response objectForKey:notification_constants::kNotificationId] || |
223 ![response objectForKey:notification_constants::kNotificationProfileId] || | 310 ![response objectForKey:notification_constants::kNotificationProfileId] || |
224 ![response objectForKey:notification_constants::kNotificationIncognito] || | 311 ![response objectForKey:notification_constants::kNotificationIncognito] || |
225 ![response objectForKey:notification_constants::kNotificationType]) { | 312 ![response objectForKey:notification_constants::kNotificationType]) { |
226 LOG(ERROR) << "Missing required key"; | 313 LOG(ERROR) << "Missing required key"; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 std::string notificationOrigin = base::SysNSStringToUTF8(origin); | 362 std::string notificationOrigin = base::SysNSStringToUTF8(origin); |
276 GURL url(notificationOrigin); | 363 GURL url(notificationOrigin); |
277 if (!url.is_valid()) | 364 if (!url.is_valid()) |
278 return false; | 365 return false; |
279 } | 366 } |
280 | 367 |
281 return true; | 368 return true; |
282 } | 369 } |
283 | 370 |
284 // ///////////////////////////////////////////////////////////////////////////// | 371 // ///////////////////////////////////////////////////////////////////////////// |
285 | |
286 @implementation NotificationCenterDelegate | 372 @implementation NotificationCenterDelegate |
287 - (void)userNotificationCenter:(NSUserNotificationCenter*)center | 373 - (void)userNotificationCenter:(NSUserNotificationCenter*)center |
288 didActivateNotification:(NSUserNotification*)notification { | 374 didActivateNotification:(NSUserNotification*)notification { |
289 NSDictionary* response = | 375 NSDictionary* notificationResponse = |
290 [NotificationResponseBuilder buildDictionary:notification]; | 376 [NotificationResponseBuilder buildDictionary:notification]; |
291 if (!NotificationPlatformBridgeMac::VerifyNotificationData(response)) | 377 NotificationPlatformBridgeMac::ProcessNotificationResponse( |
292 return; | 378 notificationResponse); |
293 | |
294 NSNumber* buttonIndex = | |
295 [response objectForKey:notification_constants::kNotificationButtonIndex]; | |
296 NSNumber* operation = | |
297 [response objectForKey:notification_constants::kNotificationOperation]; | |
298 | |
299 std::string notificationOrigin = base::SysNSStringToUTF8( | |
300 [response objectForKey:notification_constants::kNotificationOrigin]); | |
301 NSString* notificationId = | |
302 [response objectForKey:notification_constants::kNotificationId]; | |
303 std::string persistentNotificationId = | |
304 base::SysNSStringToUTF8(notificationId); | |
305 std::string profileId = base::SysNSStringToUTF8( | |
306 [response objectForKey:notification_constants::kNotificationProfileId]); | |
307 NSNumber* isIncognito = | |
308 [response objectForKey:notification_constants::kNotificationIncognito]; | |
309 NSNumber* notificationType = | |
310 [response objectForKey:notification_constants::kNotificationType]; | |
311 | |
312 ProfileManager* profileManager = g_browser_process->profile_manager(); | |
313 DCHECK(profileManager); | |
314 | |
315 profileManager->LoadProfile( | |
316 profileId, [isIncognito boolValue], | |
317 base::Bind( | |
318 &ProfileLoadedCallback, static_cast<NotificationCommon::Operation>( | |
319 operation.unsignedIntValue), | |
320 static_cast<NotificationCommon::Type>( | |
321 notificationType.unsignedIntValue), | |
322 notificationOrigin, persistentNotificationId, buttonIndex.intValue)); | |
323 } | 379 } |
324 | 380 |
325 - (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center | 381 - (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center |
326 shouldPresentNotification:(NSUserNotification*)nsNotification { | 382 shouldPresentNotification:(NSUserNotification*)nsNotification { |
327 // Always display notifications, regardless of whether the app is foreground. | 383 // Always display notifications, regardless of whether the app is foreground. |
328 return YES; | 384 return YES; |
329 } | 385 } |
330 | 386 |
331 @end | 387 @end |
| 388 |
| 389 @implementation NotificationRemoteDispatcher { |
| 390 // The connection to the XPC server in charge of delivering alerts. |
| 391 base::scoped_nsobject<NSXPCConnection> xpcConnection_; |
| 392 } |
| 393 |
| 394 - (instancetype)init { |
| 395 if ((self = [super init])) { |
| 396 xpcConnection_.reset([[NSXPCConnection alloc] |
| 397 initWithServiceName: |
| 398 [NSString |
| 399 stringWithFormat:notification_constants::kAlertXPCServiceName, |
| 400 [base::mac::OuterBundle() bundleIdentifier]]]); |
| 401 xpcConnection_.get().remoteObjectInterface = |
| 402 [NSXPCInterface interfaceWithProtocol:@protocol(NotificationDelivery)]; |
| 403 |
| 404 xpcConnection_.get().interruptionHandler = ^{ |
| 405 LOG(WARNING) << "connection interrupted: interruptionHandler: "; |
| 406 // TODO(miguelg): perhaps add some UMA here. |
| 407 // We will be getting this handler both when the XPC server crashes or |
| 408 // when it decides to close the connection. |
| 409 }; |
| 410 xpcConnection_.get().invalidationHandler = ^{ |
| 411 LOG(WARNING) << "connection invalidationHandler received"; |
| 412 // This means that the connection should be recreated if it needs |
| 413 // to be used again. It should not really happen. |
| 414 DCHECK(false) << "XPC Connection invalidated"; |
| 415 }; |
| 416 |
| 417 xpcConnection_.get().exportedInterface = |
| 418 [NSXPCInterface interfaceWithProtocol:@protocol(NotificationReply)]; |
| 419 xpcConnection_.get().exportedObject = self; |
| 420 [xpcConnection_ resume]; |
| 421 } |
| 422 |
| 423 return self; |
| 424 } |
| 425 |
| 426 - (void)dispatchNotification:(NSDictionary*)data { |
| 427 [[xpcConnection_ remoteObjectProxy] deliverNotification:data]; |
| 428 } |
| 429 |
| 430 // NotificationReply implementation |
| 431 - (void)notificationClick:(NSDictionary*)notificationResponseData { |
| 432 NotificationPlatformBridgeMac::ProcessNotificationResponse( |
| 433 notificationResponseData); |
| 434 } |
| 435 |
| 436 @end |
OLD | NEW |