Create a notification builder for MacOS
This will let us reuse the notification creation logic when migrating
to XPC services
BUG=571056
Review-Url: https://codereview.chromium.org/1981293002
Cr-Commit-Position: refs/heads/master@{#395114}
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 877d390..7f19930 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -98,6 +98,7 @@
"+third_party/WebKit/public/platform/WebLoadingBehaviorFlag.h",
"+third_party/WebKit/public/platform/WebReferrerPolicy.h",
"+third_party/WebKit/public/platform/modules/app_banner/WebAppBannerPromptReply.h",
+ "+third_party/WebKit/public/platform/modules/notifications/WebNotificationConstants.h",
"+third_party/WebKit/public/platform/modules/push_messaging/WebPushPermissionStatus.h",
"+third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationLockType.h",
"+third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h",
diff --git a/chrome/browser/notifications/notification_builder_mac.h b/chrome/browser/notifications/notification_builder_mac.h
new file mode 100644
index 0000000..692e021
--- /dev/null
+++ b/chrome/browser/notifications/notification_builder_mac.h
@@ -0,0 +1,73 @@
+// Copyright 2016 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.
+
+#ifndef CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_BUILDER_MAC_H_
+#define CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_BUILDER_MAC_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/mac/scoped_nsobject.h"
+
+@class NSUserNotification;
+
+namespace notification_builder {
+extern NSString* const kNotificationOrigin;
+extern NSString* const kNotificationId;
+extern NSString* const kNotificationProfileId;
+extern NSString* const kNotificationIncognito;
+} // notification_builder
+
+// Provides a marshallable way for storing the information required to construct
+// a NSUSerNotification that is to be displayed on the system.
+//
+// A quick example:
+// base::scoped_nsobject<NotificationBuilder> builder(
+// [[NotificationBuilder alloc] init]);
+// [builder setTitle:@"Hello"];
+//
+// // Build a notification out of the data.
+// NSUserNotification* notification =
+// [builder buildUserNotification];
+//
+// // Serialize a notification out of the data.
+// NSDictionary* notificationData = [builder buildDictionary];
+//
+// // Deserialize the |notificationData| in to a new builder.
+// base::scoped_nsobject<NotificationBuilder> finalBuilder(
+// [[NotificationBuilder alloc] initWithData:notificationData]);
+@interface NotificationBuilder : NSObject
+
+// Initializes an empty builder.
+- (instancetype)init;
+
+// Initializes a builder by deserializing |data|. The |data| must have been
+// generated by calling the buildDictionary function on another builder
+// instance.
+- (instancetype)initWithDictionary:(NSDictionary*)data;
+
+// Setters
+- (void)setTitle:(NSString*)title;
+- (void)setSubTitle:(NSString*)subTitle;
+- (void)setContextMessage:(NSString*)contextMessage;
+- (void)setIcon:(NSImage*)icon;
+- (void)setButtons:(NSString*)primaryButton
+ secondaryButton:(NSString*)secondaryButton;
+- (void)setTag:(NSString*)tag;
+- (void)setOrigin:(NSString*)origin;
+- (void)setNotificationId:(NSString*)notificationId;
+- (void)setProfileId:(NSString*)profileId;
+- (void)setIncognito:(BOOL)incognito;
+
+// Returns a notification ready to be displayed out of the provided
+// |notificationData|.
+- (NSUserNotification*)buildUserNotification;
+
+// Returns a representation of a notification that can be serialized.
+// Another instance of NotificationBuilder can read this directly and generate
+// a notification out of it via the |buildbuildUserNotification| method.
+- (NSDictionary*)buildDictionary;
+
+@end
+
+#endif // CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_BUILDER_MAC_H_
diff --git a/chrome/browser/notifications/notification_builder_mac.mm b/chrome/browser/notifications/notification_builder_mac.mm
new file mode 100644
index 0000000..321f9c3
--- /dev/null
+++ b/chrome/browser/notifications/notification_builder_mac.mm
@@ -0,0 +1,214 @@
+// Copyright 2016 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_builder_mac.h"
+
+#import <AppKit/AppKit.h>
+
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_nsobject.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+namespace {
+
+// Internal builder constants representing the different notification fields
+// They don't need to be exposed outside the builder.
+
+NSString* const kNotificationTitle = @"title";
+NSString* const kNotificationSubTitle = @"subtitle";
+NSString* const kNotificationInformativeText = @"informativeText";
+NSString* const kNotificationImage = @"icon";
+NSString* const kNotificationButtonOne = @"buttonOne";
+NSString* const kNotificationButtonTwo = @"buttonTwo";
+NSString* const kNotificationTag = @"tag";
+
+} // namespace
+
+namespace notification_builder {
+
+// Exposed constants to include user related data in the notification.
+NSString* const kNotificationOrigin = @"notificationOrigin";
+NSString* const kNotificationId = @"notificationId";
+NSString* const kNotificationProfileId = @"notificationProfileId";
+NSString* const kNotificationIncognito = @"notificationIncognito";
+
+} // namespace notification_builder
+
+@implementation NotificationBuilder {
+ base::scoped_nsobject<NSMutableDictionary> notificationData_;
+}
+
+- (instancetype)init {
+ if ((self = [super init])) {
+ notificationData_ = [[NSMutableDictionary alloc] init];
+ }
+ return self;
+}
+
+- (instancetype)initWithDictionary:(NSDictionary*)data {
+ if ((self = [super init])) {
+ notificationData_ = [data copy];
+ }
+ return self;
+}
+
+- (void)setTitle:(NSString*)title {
+ if (title.length)
+ [notificationData_ setObject:title forKey:kNotificationTitle];
+}
+
+- (void)setSubTitle:(NSString*)subTitle {
+ if (subTitle.length)
+ [notificationData_ setObject:subTitle forKey:kNotificationSubTitle];
+}
+
+- (void)setContextMessage:(NSString*)contextMessage {
+ if (contextMessage.length)
+ [notificationData_ setObject:contextMessage
+ forKey:kNotificationInformativeText];
+}
+
+- (void)setIcon:(NSImage*)icon {
+ if (icon)
+ [notificationData_ setObject:icon forKey:kNotificationImage];
+}
+
+- (void)setButtons:(NSString*)primaryButton
+ secondaryButton:(NSString*)secondaryButton {
+ DCHECK(primaryButton.length);
+ [notificationData_ setObject:primaryButton forKey:kNotificationButtonOne];
+ if (secondaryButton.length) {
+ [notificationData_ setObject:secondaryButton forKey:kNotificationButtonTwo];
+ }
+}
+
+- (void)setTag:(NSString*)tag {
+ if (tag.length)
+ [notificationData_ setObject:tag forKey:kNotificationTag];
+}
+
+- (void)setOrigin:(NSString*)origin {
+ if (origin.length)
+ [notificationData_ setObject:origin
+ forKey:notification_builder::kNotificationOrigin];
+}
+
+- (void)setNotificationId:(NSString*)notificationId {
+ DCHECK(notificationId.length);
+ [notificationData_ setObject:notificationId
+ forKey:notification_builder::kNotificationId];
+}
+
+- (void)setProfileId:(NSString*)profileId {
+ DCHECK(profileId.length);
+ [notificationData_ setObject:profileId
+ forKey:notification_builder::kNotificationProfileId];
+}
+
+- (void)setIncognito:(BOOL)incognito {
+ [notificationData_ setObject:[NSNumber numberWithBool:incognito]
+ forKey:notification_builder::kNotificationIncognito];
+}
+
+- (NSUserNotification*)buildUserNotification {
+ base::scoped_nsobject<NSUserNotification> toast(
+ [[NSUserNotification alloc] init]);
+ [toast setTitle:[notificationData_ objectForKey:kNotificationTitle]];
+ [toast setSubtitle:[notificationData_ objectForKey:kNotificationSubTitle]];
+ [toast setInformativeText:[notificationData_
+ objectForKey:kNotificationInformativeText]];
+
+ // Icon
+ if ([notificationData_ objectForKey:kNotificationImage]) {
+ if ([toast respondsToSelector:@selector(_identityImage)]) {
+ NSImage* image = [notificationData_ objectForKey:kNotificationImage];
+ [toast setValue:image forKey:@"_identityImage"];
+ [toast setValue:@NO forKey:@"_identityImageHasBorder"];
+ }
+ }
+
+ // Buttons
+ if ([toast respondsToSelector:@selector(_showsButtons)]) {
+ [toast setValue:@YES forKey:@"_showsButtons"];
+ // A default close button label is provided by the platform but we
+ // explicitly override it in case the user decides to not
+ // use the OS language in Chrome.
+ [toast setOtherButtonTitle:l10n_util::GetNSString(
+ IDS_NOTIFICATION_BUTTON_CLOSE)];
+
+ // Display the Settings button as the action button if there are either no
+ // developer-provided action buttons, or the alternate action menu is not
+ // available on this Mac version. This avoids needlessly showing the menu.
+ // TODO(miguelg): Extensions should not have a settings button.
+ if (![notificationData_ objectForKey:kNotificationButtonOne] ||
+ ![toast respondsToSelector:@selector(_alwaysShowAlternateActionMenu)]) {
+ [toast setActionButtonTitle:l10n_util::GetNSString(
+ IDS_NOTIFICATION_BUTTON_SETTINGS)];
+ } else {
+ // Otherwise show the alternate menu, then show the developer actions and
+ // finally the settings one.
+ DCHECK(
+ [toast respondsToSelector:@selector(_alwaysShowAlternateActionMenu)]);
+ DCHECK(
+ [toast respondsToSelector:@selector(_alternateActionButtonTitles)]);
+ [toast setActionButtonTitle:l10n_util::GetNSString(
+ IDS_NOTIFICATION_BUTTON_OPTIONS)];
+ [toast setValue:@YES forKey:@"_alwaysShowAlternateActionMenu"];
+
+ NSMutableArray* buttons = [NSMutableArray arrayWithCapacity:3];
+ [buttons
+ addObject:[notificationData_ objectForKey:kNotificationButtonOne]];
+ if ([notificationData_ objectForKey:kNotificationButtonTwo]) {
+ [buttons
+ addObject:[notificationData_ objectForKey:kNotificationButtonTwo]];
+ }
+ [buttons
+ addObject:l10n_util::GetNSString(IDS_NOTIFICATION_BUTTON_SETTINGS)];
+ [toast setValue:buttons forKey:@"_alternateActionButtonTitles"];
+ }
+ }
+
+ // Tag
+ if ([toast respondsToSelector:@selector(setIdentifier:)] &&
+ [notificationData_ objectForKey:kNotificationTag]) {
+ [toast setValue:[notificationData_ objectForKey:kNotificationTag]
+ forKey:@"identifier"];
+ }
+
+ NSString* origin =
+ [notificationData_ objectForKey:notification_builder::kNotificationOrigin]
+ ? [notificationData_
+ objectForKey:notification_builder::kNotificationOrigin]
+ : @"";
+ DCHECK(
+ [notificationData_ objectForKey:notification_builder::kNotificationId]);
+ NSString* notificationId =
+ [notificationData_ objectForKey:notification_builder::kNotificationId];
+
+ DCHECK([notificationData_
+ objectForKey:notification_builder::kNotificationProfileId]);
+ NSString* profileId = [notificationData_
+ objectForKey:notification_builder::kNotificationProfileId];
+
+ DCHECK([notificationData_
+ objectForKey:notification_builder::kNotificationIncognito]);
+ NSNumber* incognito = [notificationData_
+ objectForKey:notification_builder::kNotificationIncognito];
+
+ toast.get().userInfo = @{
+ notification_builder::kNotificationOrigin : origin,
+ notification_builder::kNotificationId : notificationId,
+ notification_builder::kNotificationProfileId : profileId,
+ notification_builder::kNotificationIncognito : incognito,
+ };
+
+ return toast.autorelease();
+}
+
+- (NSDictionary*)buildDictionary {
+ return [[notificationData_ copy] autorelease];
+}
+
+@end
diff --git a/chrome/browser/notifications/notification_builder_mac_unittest.mm b/chrome/browser/notifications/notification_builder_mac_unittest.mm
new file mode 100644
index 0000000..0737f33
--- /dev/null
+++ b/chrome/browser/notifications/notification_builder_mac_unittest.mm
@@ -0,0 +1,149 @@
+// Copyright 2016 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.
+
+#import <AppKit/AppKit.h>
+
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#include "chrome/browser/notifications/notification_builder_mac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(NotificationBuilderMacTest, TestNotificationNoButtons) {
+ base::scoped_nsobject<NotificationBuilder> builder(
+ [[NotificationBuilder alloc] init]);
+ [builder setTitle:@"Title"];
+ [builder setSubTitle:@""];
+ [builder setContextMessage:@"https://www.miguel.com"];
+ [builder setTag:@"tag1"];
+ [builder setIcon:[NSImage imageNamed:@"NSApplicationIcon"]];
+ [builder setNotificationId:@"notificationId"];
+ [builder setProfileId:@"profileId"];
+ [builder setIncognito:false];
+
+ NSUserNotification* notification = [builder buildUserNotification];
+ EXPECT_EQ("Title", base::SysNSStringToUTF8([notification title]));
+ EXPECT_EQ(nullptr, [notification subtitle]);
+ EXPECT_EQ("https://www.miguel.com",
+ base::SysNSStringToUTF8([notification informativeText]));
+ EXPECT_EQ("tag1",
+ base::SysNSStringToUTF8([notification valueForKey:@"identifier"]));
+
+ EXPECT_TRUE([notification hasActionButton]);
+ EXPECT_EQ("Settings",
+ base::SysNSStringToUTF8([notification actionButtonTitle]));
+ EXPECT_EQ("Close", base::SysNSStringToUTF8([notification otherButtonTitle]));
+}
+
+TEST(NotificationBuilderMacTest, TestNotificationOneButton) {
+ base::scoped_nsobject<NotificationBuilder> builder(
+ [[NotificationBuilder alloc] init]);
+ [builder setTitle:@"Title"];
+ [builder setSubTitle:@"SubTitle"];
+ [builder setContextMessage:@"https://www.miguel.com"];
+ [builder setButtons:@"Button1" secondaryButton:@""];
+ [builder setNotificationId:@"notificationId"];
+ [builder setProfileId:@"profileId"];
+ [builder setIncognito:false];
+
+ NSUserNotification* notification = [builder buildUserNotification];
+
+ EXPECT_EQ("Title", base::SysNSStringToUTF8([notification title]));
+ EXPECT_EQ("SubTitle", base::SysNSStringToUTF8([notification subtitle]));
+ EXPECT_EQ("https://www.miguel.com",
+ base::SysNSStringToUTF8([notification informativeText]));
+
+ EXPECT_TRUE([notification hasActionButton]);
+
+ EXPECT_EQ("Options",
+ base::SysNSStringToUTF8([notification actionButtonTitle]));
+ EXPECT_EQ("Close", base::SysNSStringToUTF8([notification otherButtonTitle]));
+
+ NSArray* buttons = [notification valueForKey:@"_alternateActionButtonTitles"];
+ ASSERT_EQ(2u, buttons.count);
+ EXPECT_EQ("Button1", base::SysNSStringToUTF8([buttons objectAtIndex:0]));
+ EXPECT_EQ("Settings", base::SysNSStringToUTF8([buttons objectAtIndex:1]));
+}
+
+TEST(NotificationBuilderMacTest, TestNotificationTwoButtons) {
+ base::scoped_nsobject<NotificationBuilder> builder(
+ [[NotificationBuilder alloc] init]);
+ [builder setTitle:@"Title"];
+ [builder setSubTitle:@"SubTitle"];
+ [builder setContextMessage:@"https://www.miguel.com"];
+ [builder setButtons:@"Button1" secondaryButton:@"Button2"];
+ [builder setNotificationId:@"notificationId"];
+ [builder setProfileId:@"profileId"];
+ [builder setIncognito:false];
+
+ NSUserNotification* notification = [builder buildUserNotification];
+
+ EXPECT_EQ("Title", base::SysNSStringToUTF8([notification title]));
+ EXPECT_EQ("SubTitle", base::SysNSStringToUTF8([notification subtitle]));
+ EXPECT_EQ("https://www.miguel.com",
+ base::SysNSStringToUTF8([notification informativeText]));
+
+ EXPECT_TRUE([notification hasActionButton]);
+
+ EXPECT_EQ("Options",
+ base::SysNSStringToUTF8([notification actionButtonTitle]));
+ EXPECT_EQ("Close", base::SysNSStringToUTF8([notification otherButtonTitle]));
+
+ NSArray* buttons = [notification valueForKey:@"_alternateActionButtonTitles"];
+ ASSERT_EQ(3u, buttons.count);
+ EXPECT_EQ("Button1", base::SysNSStringToUTF8([buttons objectAtIndex:0]));
+ EXPECT_EQ("Button2", base::SysNSStringToUTF8([buttons objectAtIndex:1]));
+ EXPECT_EQ("Settings", base::SysNSStringToUTF8([buttons objectAtIndex:2]));
+}
+
+TEST(NotificationBuilderMacTest, TestUserInfo) {
+ base::scoped_nsobject<NotificationBuilder> builder(
+ [[NotificationBuilder alloc] init]);
+ [builder setTitle:@"Title"];
+ [builder setProfileId:@"Profile1"];
+ [builder setOrigin:@"https://www.miguel.com"];
+ [builder setNotificationId:@"Notification1"];
+ [builder setIncognito:true];
+
+ NSUserNotification* notification = [builder buildUserNotification];
+ EXPECT_EQ("Title", base::SysNSStringToUTF8([notification title]));
+
+ NSDictionary* userInfo = [notification userInfo];
+
+ EXPECT_EQ("https://www.miguel.com",
+ base::SysNSStringToUTF8([userInfo
+ objectForKey:notification_builder::kNotificationOrigin]));
+ EXPECT_EQ("Notification1",
+ base::SysNSStringToUTF8(
+ [userInfo objectForKey:notification_builder::kNotificationId]));
+ EXPECT_EQ("Profile1",
+ base::SysNSStringToUTF8([userInfo
+ objectForKey:notification_builder::kNotificationProfileId]));
+ EXPECT_TRUE([[userInfo
+ objectForKey:notification_builder::kNotificationIncognito] boolValue]);
+}
+
+TEST(NotificationBuilderMacTest, TestBuildDictionary) {
+ NSDictionary* notificationData;
+ {
+ base::scoped_nsobject<NotificationBuilder> sourceBuilder(
+ [[NotificationBuilder alloc] init]);
+ [sourceBuilder setTitle:@"Title"];
+ [sourceBuilder setSubTitle:@"SubTitle"];
+ [sourceBuilder setContextMessage:@"https://www.miguel.com"];
+ [sourceBuilder setNotificationId:@"notificationId"];
+ [sourceBuilder setProfileId:@"profileId"];
+ [sourceBuilder setIncognito:false];
+ notificationData = [sourceBuilder buildDictionary];
+ }
+ base::scoped_nsobject<NotificationBuilder> finalBuilder(
+ [[NotificationBuilder alloc] initWithDictionary:notificationData]);
+
+ NSUserNotification* notification = [finalBuilder buildUserNotification];
+
+ EXPECT_EQ("Title", base::SysNSStringToUTF8([notification title]));
+ EXPECT_EQ("SubTitle", base::SysNSStringToUTF8([notification subtitle]));
+ EXPECT_EQ("https://www.miguel.com",
+ base::SysNSStringToUTF8([notification informativeText]));
+}
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index 8faed66..c80a191 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -12,12 +12,14 @@
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/notifications/notification.h"
+#include "chrome/browser/notifications/notification_builder_mac.h"
#include "chrome/browser/notifications/notification_display_service_factory.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/grit/generated_resources.h"
+#include "third_party/WebKit/public/platform/modules/notifications/WebNotificationConstants.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "url/gurl.h"
@@ -40,18 +42,6 @@
// - Sound names can be implemented by setting soundName in NSUserNotification
// NSUserNotificationDefaultSoundName gives you the platform default.
-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 kNotificationProfilePersistentIdKey =
- @"notification_profile_persistent_id";
-NSString* const kNotificationIncognitoKey = @"notification_incognito";
-
-} // namespace
// static
NotificationPlatformBridge* NotificationPlatformBridge::Create() {
@@ -86,10 +76,11 @@
const std::string& profile_id,
bool incognito,
const Notification& notification) {
- base::scoped_nsobject<NSUserNotification> toast(
- [[NSUserNotification alloc] init]);
- [toast setTitle:base::SysUTF16ToNSString(notification.title())];
- [toast setSubtitle:base::SysUTF16ToNSString(notification.message())];
+ base::scoped_nsobject<NotificationBuilder> builder(
+ [[NotificationBuilder alloc] init]);
+
+ [builder setTitle:base::SysUTF16ToNSString(notification.title())];
+ [builder setSubTitle:base::SysUTF16ToNSString(notification.message())];
// TODO(miguelg): try to elide the origin perhaps See NSString
// stringWithFormat. It seems that the informativeText font is constant.
@@ -97,62 +88,29 @@
notification.context_message().empty()
? base::SysUTF8ToNSString(notification.origin_url().spec())
: base::SysUTF16ToNSString(notification.context_message());
- [toast setInformativeText:informative_text];
- // Some functionality requires private APIs
- // Icon
- if ([toast respondsToSelector:@selector(_identityImage)] &&
- !notification.icon().IsEmpty()) {
- [toast setValue:notification.icon().ToNSImage() forKey:@"_identityImage"];
- [toast setValue:@NO forKey:@"_identityImageHasBorder"];
+ [builder setContextMessage:informative_text];
+ if (!notification.icon().IsEmpty()) {
+ [builder setIcon:notification.icon().ToNSImage()];
}
- // Buttons
- if ([toast respondsToSelector:@selector(_showsButtons)]) {
- [toast setValue:@YES forKey:@"_showsButtons"];
- // A default close button label is provided by the platform but we
- // explicitly override it in case the user decides to not
- // use the OS language in Chrome.
- [toast setOtherButtonTitle:l10n_util::GetNSString(
- IDS_NOTIFICATION_BUTTON_CLOSE)];
-
- // Display the Settings button as the action button if there either are no
- // developer-provided action buttons, or the alternate action menu is not
- // available on this Mac version. This avoids needlessly showing the menu.
- if (notification.buttons().empty() ||
- ![toast respondsToSelector:@selector(_alwaysShowAlternateActionMenu)]) {
- [toast setActionButtonTitle:l10n_util::GetNSString(
- IDS_NOTIFICATION_BUTTON_SETTINGS)];
- } else {
- // Otherwise show the alternate menu, then show the developer actions and
- // finally the settings one.
- DCHECK(
- [toast respondsToSelector:@selector(_alwaysShowAlternateActionMenu)]);
- DCHECK(
- [toast respondsToSelector:@selector(_alternateActionButtonTitles)]);
-
- [toast setActionButtonTitle:l10n_util::GetNSString(
- IDS_NOTIFICATION_BUTTON_OPTIONS)];
- [toast setValue:@YES forKey:@"_alwaysShowAlternateActionMenu"];
-
- NSMutableArray* buttons = [NSMutableArray arrayWithCapacity:3];
- for (const auto& action : notification.buttons())
- [buttons addObject:base::SysUTF16ToNSString(action.title)];
- [buttons
- addObject:l10n_util::GetNSString(IDS_NOTIFICATION_BUTTON_SETTINGS)];
-
- [toast setValue:buttons forKey:@"_alternateActionButtonTitles"];
- }
+ std::vector<message_center::ButtonInfo> buttons = notification.buttons();
+ if (!buttons.empty()) {
+ DCHECK_LE(buttons.size(), blink::kWebNotificationMaxActions);
+ NSString* buttonOne = SysUTF16ToNSString(buttons[0].title);
+ NSString* buttonTwo = nullptr;
+ if (buttons.size() > 1)
+ buttonTwo = SysUTF16ToNSString(buttons[1].title);
+ [builder setButtons:buttonOne secondaryButton:buttonTwo];
}
// Tag
- if ([toast respondsToSelector:@selector(setIdentifier:)] &&
- !notification.tag().empty()) {
- [toast setValue:base::SysUTF8ToNSString(notification.tag())
- forKey:@"identifier"];
-
+ if (!notification.tag().empty()) {
+ [builder setTag:base::SysUTF8ToNSString(notification.tag())];
// If renotify is needed, delete the notification with the same tag
// from the notification center before displaying this one.
+ // TODO(miguelg): This will need to work for alerts as well via XPC
+ // once supported.
if (notification.renotify()) {
NSUserNotificationCenter* notification_center =
[NSUserNotificationCenter defaultUserNotificationCenter];
@@ -169,13 +127,12 @@
}
}
- toast.get().userInfo = @{
- kNotificationOriginKey :
- base::SysUTF8ToNSString(notification.origin_url().spec()),
- kNotificationPersistentIdKey : base::SysUTF8ToNSString(notification_id),
- kNotificationProfilePersistentIdKey : base::SysUTF8ToNSString(profile_id),
- kNotificationIncognitoKey : [NSNumber numberWithBool:incognito]
- };
+ [builder setOrigin:base::SysUTF8ToNSString(notification.origin_url().spec())];
+ [builder setNotificationId:base::SysUTF8ToNSString(notification_id)];
+ [builder setProfileId:base::SysUTF8ToNSString(profile_id)];
+ [builder setIncognito:incognito];
+
+ NSUserNotification* toast = [builder buildUserNotification];
[notification_center_ deliverNotification:toast];
}
@@ -188,10 +145,10 @@
for (NSUserNotification* toast in
[notification_center_ deliveredNotifications]) {
NSString* toast_id =
- [toast.userInfo objectForKey:kNotificationPersistentIdKey];
+ [toast.userInfo objectForKey:notification_builder::kNotificationId];
- NSString* persistent_profile_id =
- [toast.userInfo objectForKey:kNotificationProfilePersistentIdKey];
+ NSString* persistent_profile_id = [toast.userInfo
+ objectForKey:notification_builder::kNotificationProfileId];
if (toast_id == candidate_id &&
persistent_profile_id == current_profile_id) {
@@ -208,11 +165,11 @@
NSString* current_profile_id = base::SysUTF8ToNSString(profile_id);
for (NSUserNotification* toast in
[notification_center_ deliveredNotifications]) {
- NSString* toast_profile_id =
- [toast.userInfo objectForKey:kNotificationProfilePersistentIdKey];
+ NSString* toast_profile_id = [toast.userInfo
+ objectForKey:notification_builder::kNotificationProfileId];
if (toast_profile_id == current_profile_id) {
notifications->insert(base::SysNSStringToUTF8(
- [toast.userInfo objectForKey:kNotificationPersistentIdKey]));
+ [toast.userInfo objectForKey:notification_builder::kNotificationId]));
}
}
return true;
@@ -227,14 +184,15 @@
@implementation NotificationCenterDelegate
- (void)userNotificationCenter:(NSUserNotificationCenter*)center
didActivateNotification:(NSUserNotification*)notification {
- std::string notificationOrigin = base::SysNSStringToUTF8(
- [notification.userInfo objectForKey:kNotificationOriginKey]);
- NSNumber* persistentNotificationId =
- [notification.userInfo objectForKey:kNotificationPersistentIdKey];
- NSString* persistentProfileId =
- [notification.userInfo objectForKey:kNotificationProfilePersistentIdKey];
- NSNumber* isIncognito =
- [notification.userInfo objectForKey:kNotificationIncognitoKey];
+ std::string notificationOrigin =
+ base::SysNSStringToUTF8([notification.userInfo
+ objectForKey:notification_builder::kNotificationOrigin]);
+ NSNumber* notificationId = [notification.userInfo
+ objectForKey:notification_builder::kNotificationId];
+ NSString* profileId = [notification.userInfo
+ objectForKey:notification_builder::kNotificationProfileId];
+ NSNumber* isIncognito = [notification.userInfo
+ objectForKey:notification_builder::kNotificationIncognito];
GURL origin(notificationOrigin);
@@ -280,9 +238,9 @@
PlatformNotificationServiceImpl::GetInstance()
->ProcessPersistentNotificationOperation(
- operation, base::SysNSStringToUTF8(persistentProfileId),
- [isIncognito boolValue], origin,
- persistentNotificationId.longLongValue, buttonIndex);
+ operation, base::SysNSStringToUTF8(profileId),
+ [isIncognito boolValue], origin, notificationId.longLongValue,
+ buttonIndex);
}
- (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 50a3313..b9df5df 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2191,6 +2191,8 @@
'browser/notifications/native_notification_display_service.h',
'browser/notifications/notification.cc',
'browser/notifications/notification.h',
+ 'browser/notifications/notification_builder_mac.mm',
+ 'browser/notifications/notification_builder_mac.h',
'browser/notifications/notification_delegate.h',
'browser/notifications/notification_display_service.h',
'browser/notifications/notification_display_service_factory.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 59be689..5c3daf5 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -558,6 +558,7 @@
'browser/media/cast_transport_host_filter_unittest.cc',
'browser/metrics/extensions_metrics_provider_unittest.cc',
'browser/notifications/extension_welcome_notification_unittest.cc',
+ 'browser/notifications/notification_builder_mac_unittest.mm',
'browser/notifications/notification_conversion_helper_unittest.cc',
'browser/renderer_context_menu/context_menu_content_type_unittest.cc',
'browser/search/hotword_service_unittest.cc',