gecko/toolkit/components/alerts/mac/mozNotificationCenterDelegate.mm

194 lines
6.0 KiB
Plaintext

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#import "mozNotificationCenterDelegate.h"
#import "ObserverPair.h"
#include "nsIObserver.h"
#include "nsIXULAppInfo.h"
#include "nsIStringBundle.h"
#include "nsIDOMWindow.h"
#include "nsObjCExceptions.h"
// Note that all calls to the Notification Center API are not made normally.
// We do this to maintain backwards compatibility.
id<FakeNSUserNotificationCenter> GetNotificationCenter() {
Class c = NSClassFromString(@"NSUserNotificationCenter");
return [c performSelector:@selector(defaultUserNotificationCenter)];
}
@implementation mozNotificationCenterDelegate
- (id)init
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
if ((self = [super init])) {
mCenter = [GetNotificationCenter() retain];
mKey = 0;
mObserverDict = [[NSMutableDictionary dictionaryWithCapacity:8] retain];
}
return self;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
- (void)dealloc
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
[mObserverDict release];
[mCenter release];
[super dealloc];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
-(void) notifyWithTitle:(const nsAString&)aTitle
description:(const nsAString&)aText
key:(uint32_t)aKey
cookie:(const nsAString&)aCookie;
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
Class unClass = NSClassFromString(@"NSUserNotification");
id<FakeNSUserNotification> notification = [[unClass alloc] init];
notification.title = [NSString stringWithCharacters:aTitle.BeginReading()
length:aTitle.Length()];
notification.informativeText = [NSString stringWithCharacters:aText.BeginReading()
length:aText.Length()];
NSDictionary* clickContext = nil;
if (aKey) {
clickContext = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt:aKey],
OBSERVER_KEY,
[NSArray arrayWithObject:
[NSString stringWithCharacters:aCookie.BeginReading()
length:aCookie.Length()]],
COOKIE_KEY,
nil];
}
notification.userInfo = clickContext;
notification.soundName = NSUserNotificationDefaultSoundName;
notification.hasActionButton = NO;
[GetNotificationCenter() deliverNotification:notification];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (uint32_t) addObserver:(nsIObserver *)aObserver
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
nsCOMPtr<nsIDOMWindow> parentWindow = GetWindowOfObserver(aObserver);
ObserverPair* pair = [[ObserverPair alloc] initWithObserver:aObserver
window:parentWindow];
[mObserverDict setObject:pair forKey:[NSNumber numberWithUnsignedInt:++mKey]];
[pair release];
return mKey;
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
}
- (void) forgetObserversForWindow:(nsIDOMWindow*)window
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
NS_ASSERTION(window, "No window!");
NSMutableArray *keysToRemove = [NSMutableArray array];
NSEnumerator *keyEnumerator = [[mObserverDict allKeys] objectEnumerator];
NSNumber *key;
while ((key = [keyEnumerator nextObject])) {
ObserverPair *pair = [mObserverDict objectForKey:key];
if (pair && pair->window == window)
[keysToRemove addObject:key];
}
[mObserverDict removeObjectsForKeys:keysToRemove];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (void) forgetObservers
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
[mObserverDict removeAllObjects];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// Implement NSUserNotificationCenterDelegate
- (void)userNotificationCenter:(id<FakeNSUserNotificationCenter>)center
didDeliverNotification:(id<FakeNSUserNotification>)notification
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
// Internally we have two notifications, "alertclickcallback" and "alertfinished".
// Notification Center does not tell us when the alert finished, but we always need
// to send alert finished, or otherwise consider it finished in order to free up
// observers. This is a conundrum. For now we'll do the unfortunately conservative
// thing and not support actions so that we can consistently end alerts when they
// are shown.
NSDictionary* userInfo = notification.userInfo;
NS_ASSERTION([userInfo valueForKey:OBSERVER_KEY] != nil,
"OBSERVER_KEY not found!");
NS_ASSERTION([userInfo valueForKey:COOKIE_KEY] != nil,
"COOKIE_KEY not found!");
ObserverPair* pair = [mObserverDict objectForKey:[userInfo valueForKey:OBSERVER_KEY]];
nsCOMPtr<nsIObserver> observer = pair ? pair->observer : nullptr;
[mObserverDict removeObjectForKey:[userInfo valueForKey:OBSERVER_KEY]];
if (observer) {
NSString* cookie = [[userInfo valueForKey:COOKIE_KEY] objectAtIndex:0];
nsAutoString tmp;
tmp.SetLength([cookie length]);
[cookie getCharacters:tmp.BeginWriting()];
observer->Observe(nullptr, "alertfinished", tmp.get());
}
// We don't want alerts to persist in the notification center because they
// are likely to refer to potentially disappearing contexts like a download.
//
// However, since we don't react to clicks right now, it is OK to let them
// persist.
//[mCenter removeDeliveredNotification:notification];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (void)userNotificationCenter:(id<FakeNSUserNotificationCenter>)center
didActivateNotification:(id<FakeNSUserNotification>)notification
{
// We don't support activation right now because it'd be sent after
// we send 'alertfinished' to observers. This is unfortunate but necessary
// because we can't reliably know when a notification is finished, we
// can only know when it is shown or activated.
}
- (BOOL)userNotificationCenter:(id<FakeNSUserNotificationCenter>)center
shouldPresentNotification:(id<FakeNSUserNotification>)notification
{
// Respect OS choice.
return NO;
}
@end