/* 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 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 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 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)center didDeliverNotification:(id)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 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)center didActivateNotification:(id)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)center shouldPresentNotification:(id)notification { // Respect OS choice. return NO; } @end