Chromium Code Reviews| Index: chrome/browser/notifications/notification_ui_manager_mac.mm |
| diff --git a/chrome/browser/notifications/notification_ui_manager_mac.mm b/chrome/browser/notifications/notification_ui_manager_mac.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2f69027839a55aca4622518e3bba7fb5da864739 |
| --- /dev/null |
| +++ b/chrome/browser/notifications/notification_ui_manager_mac.mm |
| @@ -0,0 +1,259 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/notifications/notification_ui_manager_mac.h" |
| + |
| +#include <utility> |
| + |
| +#include "base/command_line.h" |
| +#include "base/mac/foundation_util.h" |
| +#include "base/mac/mac_util.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/strings/sys_string_conversions.h" |
| +#include "chrome/browser/notifications/notification.h" |
| +#include "chrome/browser/notifications/persistent_notification_delegate.h" |
| +#include "chrome/browser/notifications/platform_notification_service_impl.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/profiles/profile_manager.h" |
| +#include "chrome/common/chrome_switches.h" |
| +#include "url/gurl.h" |
| + |
| +// The mapping from web notifications to NsUserNotification works as follows |
| + |
| +// notification#title in NSUserNotification.title |
| +// notification#message in NSUserNotification.subtitle |
| +// notification#context_message in NSUserNotification.informativeText |
| +// notification#tag in NSUserNotification.identifier (10.9) |
| +// notification#icon in NSUserNotification.contentImage (10.9) |
| + |
| +// TODO(miguelg) implement the following features |
| +// - 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.
|
| +// - 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.
|
| +// notification and adding it again in "notification center only mode" in |
| +// the shouldPresentNotification delegate method. |
| +// This also requires refactoring the popup_timer class so it does not require |
| +// a notification center. |
| +// - One Action can be implemented using the actionButton |
| +// - more than one action is only possible in 10.10 and using a private API. |
| + |
| +namespace { |
| + |
| +// Keys in NSUserNotification.userInfo to map chrome notifications to |
| +// native ones. |
| +NSString* const kNotificationTagKey = @"notification_tag"; |
| +NSString* const kNotificationOriginKey = @"notification_origin"; |
| +NSString* const kNotificationPersistentIdKey = @"notification_persistent_id"; |
| +NSString* const kNotificationDelegateIdKey = @"notification_delegate_id"; |
| +NSString* const kNotificationProfileIdKey = @"notification_profile_id"; |
| + |
| +} // namespace |
| + |
| +// static |
| +NotificationUIManager* |
| +NotificationUIManager::CreateNativeNotificationManager() { |
| + return new NotificationUIManagerMac(); |
| +} |
| + |
| +// A Cocoa class that represents the delegate of NSUserNotificationCenter and |
| +// can forward commands to C++. |
| +@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
|
| + : NSObject<NSUserNotificationCenterDelegate> { |
| + @private |
| + NotificationUIManagerMac* manager_; // Weak, owns self. |
| +} |
| +- (id)initWithManager:(NotificationUIManagerMac*)manager; |
| +@end |
| + |
| +// ///////////////////////////////////////////////////////////////////////////// |
| + |
| +NotificationUIManagerMac::NotificationUIManagerMac() |
| + : delegate_([[NotificationCenterDelegate alloc] initWithManager:this]) { |
| + [[NSUserNotificationCenter defaultUserNotificationCenter] |
| + setDelegate:delegate_.get()]; |
| +} |
| + |
| +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.
|
| + CancelAll(); |
| +} |
| + |
| +void NotificationUIManagerMac::Add(const Notification& notification, |
| + Profile* profile) { |
| + // The Mac notification UI manager only supports Web Notifications, which |
| + // have a PersistentNotificationDelegate. The persistent id of the |
| + // notification is exposed through it's interface. |
| + PersistentNotificationDelegate* delegate = |
| + static_cast<PersistentNotificationDelegate*>(notification.delegate()); |
| + DCHECK(delegate); |
| + |
| + 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
|
| + toast.title = base::SysUTF16ToNSString(notification.title()); |
| + toast.subtitle = base::SysUTF16ToNSString(notification.message()); |
| + |
| + // TODO(miguelg): try to elide the origin perhaps See NSString |
| + // stringWithFormat. It seems that the informativeText font is constant. |
| + toast.informativeText = |
| + 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
|
| + ? base::SysUTF8ToNSString(notification.origin_url().spec()) |
| + : base::SysUTF16ToNSString(notification.context_message()); |
| + |
| + // TODO(miguelg): Implement support for buttons |
| + toast.hasActionButton = NO; |
| + |
| + // Some methods are only available in 10.9+ |
| + // 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.
|
| + if ([toast respondsToSelector:@selector(setContentImage:)]) { |
| + [toast setValue:notification.icon().ToNSImage() forKey:@"contentImage"]; |
| + } |
| + |
| + // Tag |
| + if ([toast respondsToSelector:@selector(setIdentifier:)] && |
| + !notification.tag().empty()) { |
| + [toast setValue:base::SysUTF8ToNSString(notification.tag()) |
| + forKey:@"identifier"]; |
| + } |
| + |
| + int64_t persistent_notification_id = delegate->persistent_notification_id(); |
| + 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
|
| + |
| + toast.userInfo = [NSDictionary |
| + 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.
|
| + base::SysUTF8ToNSString(notification.tag()), kNotificationTagKey, |
| + base::SysUTF8ToNSString(notification.origin_url().spec()), |
| + kNotificationOriginKey, |
| + [NSNumber numberWithLongLong:persistent_notification_id], |
| + kNotificationPersistentIdKey, |
| + base::SysUTF8ToNSString(notification.delegate_id()), |
| + kNotificationDelegateIdKey, [NSNumber numberWithLongLong:profile_id], |
| + kNotificationProfileIdKey, nil]; |
| + |
| + [[NSUserNotificationCenter defaultUserNotificationCenter] |
| + deliverNotification:toast]; |
| + 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
|
| +} |
| + |
| +bool NotificationUIManagerMac::Update(const Notification& notification, |
| + Profile* profile) { |
| + NOTREACHED(); |
| + return false; |
| +} |
| + |
| +const Notification* NotificationUIManagerMac::FindById( |
| + const std::string& delegate_id, |
| + ProfileID profile_id) const { |
| + NOTREACHED(); |
| + return nil; |
| +} |
| + |
| +bool NotificationUIManagerMac::CancelById(const std::string& delegate_id, |
| + ProfileID profile_id) { |
| + int64_t persistent_notification_id = 0; |
| + // TODO(peter): Use the |delegate_id| directly when notification ids are being |
| + // generated by content/ instead of us. |
| + if (!base::StringToInt64(delegate_id, &persistent_notification_id)) |
| + return false; |
| + |
| + for (NSUserNotification* toast in |
| + [[NSUserNotificationCenter defaultUserNotificationCenter] |
| + deliveredNotifications]) { |
| + NSNumber* toast_id = |
| + [toast.userInfo objectForKey:kNotificationPersistentIdKey]; |
| + if (toast_id.longLongValue == persistent_notification_id) { |
| + [[NSUserNotificationCenter defaultUserNotificationCenter] |
| + removeDeliveredNotification:toast]; |
| + return true; |
| + } |
| + } |
| + |
| + return false; |
| +} |
| + |
| +std::set<std::string> |
| +NotificationUIManagerMac::GetAllIdsByProfileAndSourceOrigin( |
| + ProfileID profile_id, |
| + const GURL& source) { |
| + NOTREACHED(); |
| + return std::set<std::string>(); |
| +} |
| + |
| +std::set<std::string> NotificationUIManagerMac::GetAllIdsByProfile( |
| + ProfileID profile_id) { |
| + // ProfileID in mac is not safe to use across browser restarts |
| + // Therefore because when chrome quits we cancel all pending notifications. |
| + std::set<std::string> delegate_ids; |
| + for (NSUserNotification* toast in |
| + [[NSUserNotificationCenter defaultUserNotificationCenter] |
| + deliveredNotifications]) { |
| + NSNumber* toast_profile_id = |
| + [toast.userInfo objectForKey:kNotificationProfileIdKey]; |
| + if (toast_profile_id.longLongValue == (int64_t)profile_id) { |
| + delegate_ids.insert(base::SysNSStringToUTF8( |
| + [toast.userInfo objectForKey:kNotificationDelegateIdKey])); |
| + } |
| + } |
| + return delegate_ids; |
| +} |
| + |
| +bool NotificationUIManagerMac::CancelAllBySourceOrigin( |
| + const GURL& source_origin) { |
| + NOTREACHED(); |
| + return false; |
| +} |
| + |
| +bool NotificationUIManagerMac::CancelAllByProfile(ProfileID profile_id) { |
| + NOTREACHED(); |
| + return false; |
| +} |
| + |
| +void NotificationUIManagerMac::CancelAll() { |
| + [[NSUserNotificationCenter defaultUserNotificationCenter] |
| + removeAllDeliveredNotifications]; |
| +} |
| + |
| +// Mac notifications are only supported from 10.8 onwards. |
| +bool NotificationUIManagerMac::AcceptNativeNotifications() { |
| + return base::CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kEnableNativeNotifications) && |
| + base::mac::IsOSMountainLionOrLater(); |
| +} |
| + |
| +// ///////////////////////////////////////////////////////////////////////////// |
| + |
| +@implementation NotificationCenterDelegate |
| + |
| +- (id)initWithManager:(NotificationUIManagerMac*)manager { |
| + if ((self = [super init])) { |
| + CHECK(manager); |
|
Robert Sesek
2015/12/09 16:42:25
DCHECK instead?
Miguel Garcia
2015/12/10 20:16:30
Done.
|
| + manager_ = manager; |
| + } |
| + return self; |
| +} |
| + |
| +- (void)userNotificationCenter:(NSUserNotificationCenter*)center |
| + didActivateNotification:(NSUserNotification*)notification { |
| + 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.
|
| + [notification.userInfo objectForKey:kNotificationTagKey]); |
| + std::string notification_origin = base::SysNSStringToUTF8( |
| + [notification.userInfo objectForKey:kNotificationOriginKey]); |
| + NSNumber* persistent_notification_id = |
| + [notification.userInfo objectForKey:kNotificationPersistentIdKey]; |
| + |
| + GURL origin(notification_origin); |
| + |
| + // TODO(peter): Rather than assuming that the last used profile is the |
| + // appropriate one for this notification, the used profile should be |
| + // stored as part of the notification's data. |
| + // See https://crbug.com/437574. |
| + PlatformNotificationServiceImpl::GetInstance()->OnPersistentNotificationClick( |
| + 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
|
| + persistent_notification_id.longLongValue, origin, |
| + -1 /* buttons not yet implemented */); |
| +} |
| + |
| +- (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center |
| + shouldPresentNotification:(NSUserNotification*)nsNotification { |
| + // Always display notifications, regardless of whether the app is foreground. |
| + return YES; |
| +} |
| + |
| +@end |