Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Side by Side Diff: chrome/browser/notifications/notification_ui_manager_mac.mm

Issue 1509923002: Implement native web notifications for mac (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/notifications/notification_ui_manager_mac.h"
6
7 #include <utility>
8
9 #include "base/command_line.h"
10 #include "base/mac/foundation_util.h"
11 #include "base/mac/mac_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "chrome/browser/notifications/notification.h"
15 #include "chrome/browser/notifications/persistent_notification_delegate.h"
16 #include "chrome/browser/notifications/platform_notification_service_impl.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "url/gurl.h"
21
22 // The mapping from web notifications to NsUserNotification works as follows
23
24 // notification#title in NSUserNotification.title
25 // notification#message in NSUserNotification.subtitle
26 // notification#context_message in NSUserNotification.informativeText
27 // notification#tag in NSUserNotification.identifier (10.9)
28 // notification#icon in NSUserNotification.contentImage (10.9)
29
30 // TODO(miguelg) implement the following features
31 // - Sound names can be implemented by setting soundName in NSUserNotification
Peter Beverloo 2015/12/09 00:12:00 What would we use sounds for? Getting attention? C
Miguel Garcia 2015/12/10 20:16:30 Yes, will change the todo accordingly.
32 // - notifiation.requireInteraction can be implemented by removing the
Peter Beverloo 2015/12/09 00:12:00 nit: s/notifiation/notification/
Miguel Garcia 2015/12/10 20:16:30 Done.
33 // notification and adding it again in "notification center only mode" in
34 // the shouldPresentNotification delegate method.
35 // This also requires refactoring the popup_timer class so it does not require
36 // a notification center.
37 // - One Action can be implemented using the actionButton
38 // - more than one action is only possible in 10.10 and using a private API.
39
40 namespace {
41
42 // Keys in NSUserNotification.userInfo to map chrome notifications to
43 // native ones.
44 NSString* const kNotificationTagKey = @"notification_tag";
45 NSString* const kNotificationOriginKey = @"notification_origin";
46 NSString* const kNotificationPersistentIdKey = @"notification_persistent_id";
47 NSString* const kNotificationDelegateIdKey = @"notification_delegate_id";
48 NSString* const kNotificationProfileIdKey = @"notification_profile_id";
49
50 } // namespace
51
52 // static
53 NotificationUIManager*
54 NotificationUIManager::CreateNativeNotificationManager() {
55 return new NotificationUIManagerMac();
56 }
57
58 // A Cocoa class that represents the delegate of NSUserNotificationCenter and
59 // can forward commands to C++.
60 @interface NotificationCenterDelegate
Peter Beverloo 2015/12/09 00:12:00 What happens when a Chrome notification is shown i
Miguel Garcia 2015/12/10 20:16:30 It wakes up the app and once a delegate is up and
61 : NSObject<NSUserNotificationCenterDelegate> {
62 @private
63 NotificationUIManagerMac* manager_; // Weak, owns self.
64 }
65 - (id)initWithManager:(NotificationUIManagerMac*)manager;
66 @end
67
68 // /////////////////////////////////////////////////////////////////////////////
69
70 NotificationUIManagerMac::NotificationUIManagerMac()
71 : delegate_([[NotificationCenterDelegate alloc] initWithManager:this]) {
72 [[NSUserNotificationCenter defaultUserNotificationCenter]
73 setDelegate:delegate_.get()];
74 }
75
76 NotificationUIManagerMac::~NotificationUIManagerMac() {
Robert Sesek 2015/12/09 16:42:25 Set the NSUserNotificationCenter delegate to nil,
Miguel Garcia 2015/12/10 20:16:30 Done.
77 CancelAll();
78 }
79
80 void NotificationUIManagerMac::Add(const Notification& notification,
81 Profile* profile) {
82 // The Mac notification UI manager only supports Web Notifications, which
83 // have a PersistentNotificationDelegate. The persistent id of the
84 // notification is exposed through it's interface.
85 PersistentNotificationDelegate* delegate =
86 static_cast<PersistentNotificationDelegate*>(notification.delegate());
87 DCHECK(delegate);
88
89 NSUserNotification* toast = [[NSUserNotification alloc] init];
Robert Sesek 2015/12/09 16:42:25 This is leaked, so put it in a scoped_nsobject.
Miguel Garcia 2015/12/10 20:16:30 Good catch
90 toast.title = base::SysUTF16ToNSString(notification.title());
91 toast.subtitle = base::SysUTF16ToNSString(notification.message());
92
93 // TODO(miguelg): try to elide the origin perhaps See NSString
94 // stringWithFormat. It seems that the informativeText font is constant.
95 toast.informativeText =
96 notification.context_message().empty()
Peter Beverloo 2015/12/09 00:12:00 nit: This class is only functional for persistent
Miguel Garcia 2015/12/10 20:16:30 It actually does get set for extensions using the
97 ? base::SysUTF8ToNSString(notification.origin_url().spec())
98 : base::SysUTF16ToNSString(notification.context_message());
99
100 // TODO(miguelg): Implement support for buttons
101 toast.hasActionButton = NO;
102
103 // Some methods are only available in 10.9+
104 // Icon
Peter Beverloo 2015/12/09 00:12:00 nit: did you accidentally a
Miguel Garcia 2015/12/10 20:16:30 Not sure what you mean here.
105 if ([toast respondsToSelector:@selector(setContentImage:)]) {
106 [toast setValue:notification.icon().ToNSImage() forKey:@"contentImage"];
107 }
108
109 // Tag
110 if ([toast respondsToSelector:@selector(setIdentifier:)] &&
111 !notification.tag().empty()) {
112 [toast setValue:base::SysUTF8ToNSString(notification.tag())
113 forKey:@"identifier"];
114 }
115
116 int64_t persistent_notification_id = delegate->persistent_notification_id();
117 int64_t profile_id = (int64_t)GetProfileID(profile);
Robert Sesek 2015/12/09 16:42:25 Use static_cast instead.
Miguel Garcia 2015/12/10 20:16:30 static_cast does work with void* types but I can u
118
119 toast.userInfo = [NSDictionary
120 dictionaryWithObjectsAndKeys:
Robert Sesek 2015/12/09 16:42:25 You can use a dictionary literal syntax here, whic
Miguel Garcia 2015/12/10 20:16:30 Done.
121 base::SysUTF8ToNSString(notification.tag()), kNotificationTagKey,
122 base::SysUTF8ToNSString(notification.origin_url().spec()),
123 kNotificationOriginKey,
124 [NSNumber numberWithLongLong:persistent_notification_id],
125 kNotificationPersistentIdKey,
126 base::SysUTF8ToNSString(notification.delegate_id()),
127 kNotificationDelegateIdKey, [NSNumber numberWithLongLong:profile_id],
128 kNotificationProfileIdKey, nil];
129
130 [[NSUserNotificationCenter defaultUserNotificationCenter]
131 deliverNotification:toast];
132 delegate->Display();
Robert Sesek 2015/12/09 16:42:25 This should probably be called in userNotification
Miguel Garcia 2015/12/10 20:16:30 Yes, it's probably more consistent but the problem
Robert Sesek 2015/12/11 21:17:53 Can't you look the notification delegate up by tag
Miguel Garcia 2015/12/15 13:37:38 Yeah we could maintain state but even better, I re
Robert Sesek 2015/12/15 16:52:54 That's a little surprising, given that a show even
133 }
134
135 bool NotificationUIManagerMac::Update(const Notification& notification,
136 Profile* profile) {
137 NOTREACHED();
138 return false;
139 }
140
141 const Notification* NotificationUIManagerMac::FindById(
142 const std::string& delegate_id,
143 ProfileID profile_id) const {
144 NOTREACHED();
145 return nil;
146 }
147
148 bool NotificationUIManagerMac::CancelById(const std::string& delegate_id,
149 ProfileID profile_id) {
150 int64_t persistent_notification_id = 0;
151 // TODO(peter): Use the |delegate_id| directly when notification ids are being
152 // generated by content/ instead of us.
153 if (!base::StringToInt64(delegate_id, &persistent_notification_id))
154 return false;
155
156 for (NSUserNotification* toast in
157 [[NSUserNotificationCenter defaultUserNotificationCenter]
158 deliveredNotifications]) {
159 NSNumber* toast_id =
160 [toast.userInfo objectForKey:kNotificationPersistentIdKey];
161 if (toast_id.longLongValue == persistent_notification_id) {
162 [[NSUserNotificationCenter defaultUserNotificationCenter]
163 removeDeliveredNotification:toast];
164 return true;
165 }
166 }
167
168 return false;
169 }
170
171 std::set<std::string>
172 NotificationUIManagerMac::GetAllIdsByProfileAndSourceOrigin(
173 ProfileID profile_id,
174 const GURL& source) {
175 NOTREACHED();
176 return std::set<std::string>();
177 }
178
179 std::set<std::string> NotificationUIManagerMac::GetAllIdsByProfile(
180 ProfileID profile_id) {
181 // ProfileID in mac is not safe to use across browser restarts
182 // Therefore because when chrome quits we cancel all pending notifications.
183 std::set<std::string> delegate_ids;
184 for (NSUserNotification* toast in
185 [[NSUserNotificationCenter defaultUserNotificationCenter]
186 deliveredNotifications]) {
187 NSNumber* toast_profile_id =
188 [toast.userInfo objectForKey:kNotificationProfileIdKey];
189 if (toast_profile_id.longLongValue == (int64_t)profile_id) {
190 delegate_ids.insert(base::SysNSStringToUTF8(
191 [toast.userInfo objectForKey:kNotificationDelegateIdKey]));
192 }
193 }
194 return delegate_ids;
195 }
196
197 bool NotificationUIManagerMac::CancelAllBySourceOrigin(
198 const GURL& source_origin) {
199 NOTREACHED();
200 return false;
201 }
202
203 bool NotificationUIManagerMac::CancelAllByProfile(ProfileID profile_id) {
204 NOTREACHED();
205 return false;
206 }
207
208 void NotificationUIManagerMac::CancelAll() {
209 [[NSUserNotificationCenter defaultUserNotificationCenter]
210 removeAllDeliveredNotifications];
211 }
212
213 // Mac notifications are only supported from 10.8 onwards.
214 bool NotificationUIManagerMac::AcceptNativeNotifications() {
215 return base::CommandLine::ForCurrentProcess()->HasSwitch(
216 switches::kEnableNativeNotifications) &&
217 base::mac::IsOSMountainLionOrLater();
218 }
219
220 // /////////////////////////////////////////////////////////////////////////////
221
222 @implementation NotificationCenterDelegate
223
224 - (id)initWithManager:(NotificationUIManagerMac*)manager {
225 if ((self = [super init])) {
226 CHECK(manager);
Robert Sesek 2015/12/09 16:42:25 DCHECK instead?
Miguel Garcia 2015/12/10 20:16:30 Done.
227 manager_ = manager;
228 }
229 return self;
230 }
231
232 - (void)userNotificationCenter:(NSUserNotificationCenter*)center
233 didActivateNotification:(NSUserNotification*)notification {
234 std::string notification_tag = base::SysNSStringToUTF8(
Robert Sesek 2015/12/09 16:42:25 naming: camelCase
Miguel Garcia 2015/12/10 20:16:30 Done.
235 [notification.userInfo objectForKey:kNotificationTagKey]);
236 std::string notification_origin = base::SysNSStringToUTF8(
237 [notification.userInfo objectForKey:kNotificationOriginKey]);
238 NSNumber* persistent_notification_id =
239 [notification.userInfo objectForKey:kNotificationPersistentIdKey];
240
241 GURL origin(notification_origin);
242
243 // TODO(peter): Rather than assuming that the last used profile is the
244 // appropriate one for this notification, the used profile should be
245 // stored as part of the notification's data.
246 // See https://crbug.com/437574.
247 PlatformNotificationServiceImpl::GetInstance()->OnPersistentNotificationClick(
248 ProfileManager::GetLastUsedProfile(),
Peter Beverloo 2015/12/09 00:12:00 This is not a safe TODO on Mac, given that we can
Miguel Garcia 2015/12/10 20:16:30 True I will add a scarier message. We are aware th
249 persistent_notification_id.longLongValue, origin,
250 -1 /* buttons not yet implemented */);
251 }
252
253 - (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center
254 shouldPresentNotification:(NSUserNotification*)nsNotification {
255 // Always display notifications, regardless of whether the app is foreground.
256 return YES;
257 }
258
259 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698