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..7a8c96323d6b49e4d132c499a76713e1b58c33b8 |
--- /dev/null |
+++ b/chrome/browser/notifications/notification_ui_manager_mac.mm |
@@ -0,0 +1,258 @@ |
+// 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 |
+// NSUserNotificationDefaultSoundName gives you the platform default. |
+// - notification.requireInteraction can be implemented by removing the |
+// 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 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 |
+ : 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() { |
+ [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil]; |
+ 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); |
+ |
+ base::scoped_nsobject<NSUserNotification> toast( |
+ [[NSUserNotification alloc] init]); |
+ toast.get().title = base::SysUTF16ToNSString(notification.title()); |
Robert Sesek
2015/12/11 21:17:53
Do you need the .get()s everywhere? The operator T
Miguel Garcia
2015/12/15 13:37:40
Well if I do for example
toast.title = base::SysU
Robert Sesek
2015/12/15 16:52:54
Ah, yes C++ gets confused about that. If you use [
|
+ toast.get().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.get().informativeText = |
+ notification.context_message().empty() |
+ ? base::SysUTF8ToNSString(notification.origin_url().spec()) |
+ : base::SysUTF16ToNSString(notification.context_message()); |
+ |
+ // TODO(miguelg): Implement support for buttons |
+ toast.get().hasActionButton = NO; |
+ |
+ // Some functionality is only available in 10.9+ |
+ // Icon |
+ if ([toast.get() respondsToSelector:@selector(setContentImage:)]) { |
+ [toast.get() setValue:notification.icon().ToNSImage() |
+ forKey:@"contentImage"]; |
+ } |
+ |
+ // Tag |
+ if ([toast.get() respondsToSelector:@selector(setIdentifier:)] && |
+ !notification.tag().empty()) { |
+ [toast.get() setValue:base::SysUTF8ToNSString(notification.tag()) |
+ forKey:@"identifier"]; |
+ } |
+ |
+ int64_t persistent_notification_id = delegate->persistent_notification_id(); |
+ int64_t profile_id = reinterpret_cast<int64_t>(GetProfileID(profile)); |
+ |
+ toast.get().userInfo = @{ |
+ kNotificationOriginKey : |
+ base::SysUTF8ToNSString(notification.origin_url().spec()), |
+ kNotificationPersistentIdKey : |
+ [NSNumber numberWithLongLong:persistent_notification_id], |
+ kNotificationDelegateIdKey : |
+ base::SysUTF8ToNSString(notification.delegate_id()), |
+ kNotificationProfileIdKey : [NSNumber numberWithLongLong:profile_id] |
+ }; |
+ |
+ [[NSUserNotificationCenter defaultUserNotificationCenter] |
+ deliverNotification:toast.get()]; |
+ delegate->Display(); |
+} |
+ |
+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] |
Robert Sesek
2015/12/11 21:17:53
Pull the result of -defaultUserNotificationCenter
Miguel Garcia
2015/12/15 13:37:41
Done.
Meta question. How do I figure out if [NSUs
Robert Sesek
2015/12/15 16:52:54
Methods by default return weak references to objec
|
+ 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 == |
+ reinterpret_cast<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])) { |
+ DCHECK(manager); |
+ manager_ = manager; |
+ } |
+ return self; |
+} |
+ |
+- (void)userNotificationCenter:(NSUserNotificationCenter*)center |
+ didActivateNotification:(NSUserNotification*)notification { |
+ std::string notificationOrigin = base::SysNSStringToUTF8( |
+ [notification.userInfo objectForKey:kNotificationOriginKey]); |
+ NSNumber* persistentNotificationId = |
+ [notification.userInfo objectForKey:kNotificationPersistentIdKey]; |
+ |
+ GURL origin(notificationOrigin); |
+ |
+ // TODO(miguelg):We cannot ship like this since we could be |
+ // delivering messages to the wrong profile. |
+ PlatformNotificationServiceImpl::GetInstance()->OnPersistentNotificationClick( |
+ ProfileManager::GetLastUsedProfile(), |
+ persistentNotificationId.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 |