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

Unified 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 side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698