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

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 4 years, 11 months 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 @class NSUserNotification;
23 @class NSUserNotificationCenter;
24
25 // The mapping from web notifications to NsUserNotification works as follows
26
27 // notification#title in NSUserNotification.title
28 // notification#message in NSUserNotification.subtitle
29 // notification#context_message in NSUserNotification.informativeText
30 // notification#tag in NSUserNotification.identifier (10.9)
31 // notification#icon in NSUserNotification.contentImage (10.9)
32
33 // TODO(miguelg) implement the following features
34 // - Sound names can be implemented by setting soundName in NSUserNotification
35 // NSUserNotificationDefaultSoundName gives you the platform default.
36 // - notification.requireInteraction can be implemented by removing the
37 // notification and adding it again in "notification center only mode" in
38 // the shouldPresentNotification delegate method.
39 // This also requires refactoring the popup_timer class so it does not require
40 // a notification center.
41 // - One Action can be implemented using the actionButton
42 // - more than one action is only possible in 10.10 and using a private API.
43
44 namespace {
45
46 // Keys in NSUserNotification.userInfo to map chrome notifications to
47 // native ones.
48 NSString* const kNotificationOriginKey = @"notification_origin";
49 NSString* const kNotificationPersistentIdKey = @"notification_persistent_id";
50 NSString* const kNotificationDelegateIdKey = @"notification_delegate_id";
51 NSString* const kNotificationProfileIdKey = @"notification_profile_id";
52
53 } // namespace
54
55 // Only use native notifications for web, behind a flag and on 10.8+
56 // static
57 NotificationUIManager*
58 NotificationUIManager::CreateNativeNotificationManager() {
59 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
60 switches::kEnableNativeNotifications) &&
61 base::mac::IsOSMountainLionOrLater()) {
62 return new NotificationUIManagerMac();
63 }
64 return nullptr;
65 }
66
67 // A Cocoa class that represents the delegate of NSUserNotificationCenter and
68 // can forward commands to C++.
69 @interface NotificationCenterDelegate
70 : NSObject<NSUserNotificationCenterDelegate> {
71 @private
72 NotificationUIManagerMac* manager_; // Weak, owns self.
73 }
74 - (id)initWithManager:(NotificationUIManagerMac*)manager;
75 @end
76
77 // /////////////////////////////////////////////////////////////////////////////
78
79 NotificationUIManagerMac::NotificationUIManagerMac()
80 : delegate_([[NotificationCenterDelegate alloc] initWithManager:this]) {
81 [[NSUserNotificationCenter defaultUserNotificationCenter]
82 setDelegate:delegate_.get()];
83 }
84
85 NotificationUIManagerMac::~NotificationUIManagerMac() {
86 [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];
87 CancelAll();
88 }
89
90 void NotificationUIManagerMac::Add(const Notification& notification,
91 Profile* profile) {
92 // The Mac notification UI manager only supports Web Notifications, which
93 // have a PersistentNotificationDelegate. The persistent id of the
94 // notification is exposed through it's interface.
95 PersistentNotificationDelegate* delegate =
96 static_cast<PersistentNotificationDelegate*>(notification.delegate());
97 DCHECK(delegate);
98
99 base::scoped_nsobject<NSUserNotification> toast(
100 [[NSUserNotification alloc] init]);
101 [toast setTitle:base::SysUTF16ToNSString(notification.title())];
102 [toast setSubtitle:base::SysUTF16ToNSString(notification.message())];
103
104 // TODO(miguelg): try to elide the origin perhaps See NSString
105 // stringWithFormat. It seems that the informativeText font is constant.
106 NSString* informativeText =
107 notification.context_message().empty()
108 ? base::SysUTF8ToNSString(notification.origin_url().spec())
109 : base::SysUTF16ToNSString(notification.context_message());
110 [toast setInformativeText:informativeText];
111
112 // TODO(miguelg): Implement support for buttons
113 toast.get().hasActionButton = NO;
114
115 // Some functionality is only available in 10.9+
116 // Icon
117 if ([toast respondsToSelector:@selector(setContentImage:)]) {
118 [toast setValue:notification.icon().ToNSImage() forKey:@"contentImage"];
119 }
120
121 // Tag
122 if ([toast respondsToSelector:@selector(setIdentifier:)] &&
123 !notification.tag().empty()) {
124 [toast setValue:base::SysUTF8ToNSString(notification.tag())
125 forKey:@"identifier"];
126 }
127
128 int64_t persistent_notification_id = delegate->persistent_notification_id();
129 int64_t profile_id = reinterpret_cast<int64_t>(GetProfileID(profile));
130
131 toast.get().userInfo = @{
132 kNotificationOriginKey :
133 base::SysUTF8ToNSString(notification.origin_url().spec()),
134 kNotificationPersistentIdKey :
135 [NSNumber numberWithLongLong:persistent_notification_id],
136 kNotificationDelegateIdKey :
137 base::SysUTF8ToNSString(notification.delegate_id()),
138 kNotificationProfileIdKey : [NSNumber numberWithLongLong:profile_id]
139 };
140
141 [[NSUserNotificationCenter defaultUserNotificationCenter]
142 deliverNotification:toast];
143 }
144
145 bool NotificationUIManagerMac::Update(const Notification& notification,
146 Profile* profile) {
147 NOTREACHED();
148 return false;
149 }
150
151 const Notification* NotificationUIManagerMac::FindById(
152 const std::string& delegate_id,
153 ProfileID profile_id) const {
154 NOTREACHED();
155 return nil;
156 }
157
158 bool NotificationUIManagerMac::CancelById(const std::string& delegate_id,
159 ProfileID profile_id) {
160 int64_t persistent_notification_id = 0;
161 // TODO(peter): Use the |delegate_id| directly when notification ids are being
162 // generated by content/ instead of us.
163 if (!base::StringToInt64(delegate_id, &persistent_notification_id))
164 return false;
165
166 NSUserNotificationCenter* notificationCenter =
167 [NSUserNotificationCenter defaultUserNotificationCenter];
168 for (NSUserNotification* toast in
169 [notificationCenter deliveredNotifications]) {
170 NSNumber* toast_id =
171 [toast.userInfo objectForKey:kNotificationPersistentIdKey];
172 if (toast_id.longLongValue == persistent_notification_id) {
173 [notificationCenter removeDeliveredNotification:toast];
174 return true;
175 }
176 }
177
178 return false;
179 }
180
181 std::set<std::string>
182 NotificationUIManagerMac::GetAllIdsByProfileAndSourceOrigin(
183 ProfileID profile_id,
184 const GURL& source) {
185 NOTREACHED();
186 return std::set<std::string>();
187 }
188
189 std::set<std::string> NotificationUIManagerMac::GetAllIdsByProfile(
190 ProfileID profile_id) {
191 // ProfileID in mac is not safe to use across browser restarts
192 // Therefore because when chrome quits we cancel all pending notifications.
193 std::set<std::string> delegate_ids;
194 NSUserNotificationCenter* notificationCenter =
195 [NSUserNotificationCenter defaultUserNotificationCenter];
196 for (NSUserNotification* toast in
197 [notificationCenter deliveredNotifications]) {
198 NSNumber* toast_profile_id =
199 [toast.userInfo objectForKey:kNotificationProfileIdKey];
200 if (toast_profile_id.longLongValue ==
201 reinterpret_cast<int64_t>(profile_id)) {
202 delegate_ids.insert(base::SysNSStringToUTF8(
203 [toast.userInfo objectForKey:kNotificationDelegateIdKey]));
204 }
205 }
206 return delegate_ids;
207 }
208
209 bool NotificationUIManagerMac::CancelAllBySourceOrigin(
210 const GURL& source_origin) {
211 NOTREACHED();
212 return false;
213 }
214
215 bool NotificationUIManagerMac::CancelAllByProfile(ProfileID profile_id) {
216 NOTREACHED();
217 return false;
218 }
219
220 void NotificationUIManagerMac::CancelAll() {
221 [[NSUserNotificationCenter defaultUserNotificationCenter]
222 removeAllDeliveredNotifications];
223 }
224
225 // /////////////////////////////////////////////////////////////////////////////
226
227 @implementation NotificationCenterDelegate
228
229 - (id)initWithManager:(NotificationUIManagerMac*)manager {
230 if ((self = [super init])) {
231 DCHECK(manager);
232 manager_ = manager;
233 }
234 return self;
235 }
236
237 - (void)userNotificationCenter:(NSUserNotificationCenter*)center
238 didActivateNotification:(NSUserNotification*)notification {
239 std::string notificationOrigin = base::SysNSStringToUTF8(
240 [notification.userInfo objectForKey:kNotificationOriginKey]);
241 NSNumber* persistentNotificationId =
242 [notification.userInfo objectForKey:kNotificationPersistentIdKey];
243
244 GURL origin(notificationOrigin);
245
246 // TODO(miguelg):We cannot ship like this since we could be
247 // delivering messages to the wrong profile.
248 PlatformNotificationServiceImpl::GetInstance()->OnPersistentNotificationClick(
249 ProfileManager::GetLastUsedProfile(),
250 persistentNotificationId.longLongValue, origin,
251 -1 /* buttons not yet implemented */);
252 }
253
254 - (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center
255 shouldPresentNotification:(NSUserNotification*)nsNotification {
256 // Always display notifications, regardless of whether the app is foreground.
257 return YES;
258 }
259
260 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698