Bug 1233086, Part 3 - Use unified image loading for OS X alerts. r=mstange draft
authorKit Cambridge <kcambridge@mozilla.com>
Fri, 10 Jun 2016 18:59:25 -0700
changeset 381547 54c144634f5c17a747d54a7d007e5fee0a1aabe4
parent 381546 bd88f0e35c32ef634310c2b92774a790d3c9b7da
child 381548 32ce749601cf587fb7fdd3043d2193f100b37be6
push id21506
push userkcambridge@mozilla.com
push dateMon, 27 Jun 2016 18:27:43 +0000
reviewersmstange
bugs1233086
milestone50.0a1
Bug 1233086, Part 3 - Use unified image loading for OS X alerts. r=mstange MozReview-Commit-ID: Ln5rHIj8xxI
widget/cocoa/OSXNotificationCenter.h
widget/cocoa/OSXNotificationCenter.mm
--- a/widget/cocoa/OSXNotificationCenter.h
+++ b/widget/cocoa/OSXNotificationCenter.h
@@ -19,26 +19,24 @@
 typedef NSInteger NSUserNotificationActivationType;
 #endif
 
 namespace mozilla {
 
 class OSXNotificationInfo;
 
 class OSXNotificationCenter : public nsIAlertsService,
-                              public imgINotificationObserver,
-                              public nsITimerCallback,
-                              public nsIAlertsIconData
+                              public nsIAlertsIconData,
+                              public nsIAlertNotificationImageListener
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIALERTSSERVICE
-  NS_DECL_IMGINOTIFICATIONOBSERVER
-  NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSIALERTSICONDATA
+  NS_DECL_NSIALERTNOTIFICATIONIMAGELISTENER
 
   OSXNotificationCenter();
 
   nsresult Init();
   void CloseAlertCocoaString(NSString *aAlertName);
   void OnActivate(NSString *aAlertName, NSUserNotificationActivationType aActivationType,
                   unsigned long long aAdditionalActionIndex);
   void ShowPendingNotification(OSXNotificationInfo *osxni);
--- a/widget/cocoa/OSXNotificationCenter.mm
+++ b/widget/cocoa/OSXNotificationCenter.mm
@@ -2,28 +2,26 @@
 /* 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/. */
 
 #include "OSXNotificationCenter.h"
 #import <AppKit/AppKit.h>
 #include "imgIRequest.h"
 #include "imgIContainer.h"
+#include "nsICancelable.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
-#include "imgLoader.h"
 #import "nsCocoaUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsObjCExceptions.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
-#include "nsIContentPolicy.h"
-#include "imgRequestProxy.h"
 
 using namespace mozilla;
 
 #define MAX_NOTIFICATION_NAME_LEN 5000
 
 #if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8)
 @protocol NSUserNotificationCenterDelegate
 @end
@@ -145,33 +143,34 @@ enum {
 
 namespace mozilla {
 
 enum {
   OSXNotificationActionDisable = 0,
   OSXNotificationActionSettings = 1,
 };
 
-class OSXNotificationInfo {
+class OSXNotificationInfo final : public nsISupports {
 private:
-  ~OSXNotificationInfo();
+  virtual ~OSXNotificationInfo();
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(OSXNotificationInfo)
+  NS_DECL_ISUPPORTS
   OSXNotificationInfo(NSString *name, nsIObserver *observer,
                       const nsAString & alertCookie);
 
   NSString *mName;
   nsCOMPtr<nsIObserver> mObserver;
   nsString mCookie;
-  RefPtr<imgRequestProxy> mIconRequest;
+  RefPtr<nsICancelable> mIconRequest;
   id<FakeNSUserNotification> mPendingNotifiction;
-  nsCOMPtr<nsITimer> mIconTimeoutTimer;
 };
 
+NS_IMPL_ISUPPORTS0(OSXNotificationInfo)
+
 OSXNotificationInfo::OSXNotificationInfo(NSString *name, nsIObserver *observer,
                                          const nsAString & alertCookie)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   NS_ASSERTION(name, "Cannot create OSXNotificationInfo without a name!");
   mName = [name retain];
   mObserver = observer;
@@ -215,18 +214,18 @@ OSXNotificationCenter::~OSXNotificationC
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   [GetNotificationCenter() removeAllDeliveredNotifications];
   [mDelegate release];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
-NS_IMPL_ISUPPORTS(OSXNotificationCenter, nsIAlertsService, nsITimerCallback,
-                  imgINotificationObserver, nsIAlertsIconData)
+NS_IMPL_ISUPPORTS(OSXNotificationCenter, nsIAlertsService, nsIAlertsIconData,
+                  nsIAlertNotificationImageListener)
 
 nsresult OSXNotificationCenter::Init()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   return (!!NSClassFromString(@"NSUserNotification")) ? NS_OK : NS_ERROR_FAILURE;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
@@ -382,67 +381,39 @@ OSXNotificationCenter::ShowAlertWithIcon
 
     NSData *iconData = [NSData dataWithBytes:aIconData length:aIconSize];
     NSImage *icon = [[[NSImage alloc] initWithData:iconData] autorelease];
 
     [(NSObject*)notification setValue:icon forKey:@"_identityImage"];
     [(NSObject*)notification setValue:@(NO) forKey:@"_identityImageHasBorder"];
   }
 
-  nsAutoString imageUrl;
-  rv = aAlert->GetImageURL(imageUrl);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   bool inPrivateBrowsing;
   rv = aAlert->GetInPrivateBrowsing(&inPrivateBrowsing);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Show the notification without waiting for an image if there is no icon URL or
   // notification icons are not supported on this version of OS X.
-  if (imageUrl.IsEmpty() || ![unClass instancesRespondToSelector:@selector(setContentImage:)]) {
+  if (![unClass instancesRespondToSelector:@selector(setContentImage:)]) {
     CloseAlertCocoaString(alertName);
     mActiveAlerts.AppendElement(osxni);
     [GetNotificationCenter() deliverNotification:notification];
     [notification release];
     if (aAlertListener) {
       aAlertListener->Observe(nullptr, "alertshow", cookie.get());
     }
   } else {
     mPendingAlerts.AppendElement(osxni);
     osxni->mPendingNotifiction = notification;
-    imgLoader* il = inPrivateBrowsing ? imgLoader::PrivateBrowsingLoader()
-                                      : imgLoader::NormalLoader();
-    if (il) {
-      nsCOMPtr<nsIURI> imageUri;
-      NS_NewURI(getter_AddRefs(imageUri), imageUrl);
-      if (imageUri) {
-        nsCOMPtr<nsIPrincipal> principal;
-        rv = aAlert->GetPrincipal(getter_AddRefs(principal));
-        if (NS_SUCCEEDED(rv)) {
-          rv = il->LoadImage(imageUri, nullptr, nullptr,
-                             mozilla::net::RP_Default,
-                             principal, nullptr,
-                             this, nullptr, nullptr,
-                             inPrivateBrowsing ? nsIRequest::LOAD_ANONYMOUS :
-                                                 nsIRequest::LOAD_NORMAL,
-                             nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE,
-                             EmptyString(),
-                             getter_AddRefs(osxni->mIconRequest));
-          if (NS_SUCCEEDED(rv)) {
-            // Set a timer for six seconds. If we don't have an icon by the time this
-            // goes off then we go ahead without an icon.
-            nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
-            osxni->mIconTimeoutTimer = timer;
-            timer->InitWithCallback(this, 6000, nsITimer::TYPE_ONE_SHOT);
-            return NS_OK;
-          }
-        }
-      }
+    // Wait six seconds for the image to load.
+    rv = aAlert->LoadImage(6000, this, osxni,
+                           getter_AddRefs(osxni->mIconRequest));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      ShowPendingNotification(osxni);
     }
-    ShowPendingNotification(osxni);
   }
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 NS_IMETHODIMP
@@ -478,16 +449,20 @@ OSXNotificationCenter::CloseAlertCocoaSt
   }
 
   for (unsigned int i = 0; i < mActiveAlerts.Length(); i++) {
     OSXNotificationInfo *osxni = mActiveAlerts[i];
     if ([aAlertName isEqualToString:osxni->mName]) {
       if (osxni->mObserver) {
         osxni->mObserver->Observe(nullptr, "alertfinished", osxni->mCookie.get());
       }
+      if (osxni->mIconRequest) {
+        osxni->mIconRequest->Cancel(NS_BINDING_ABORTED);
+        osxni->mIconRequest = nullptr;
+      }
       mActiveAlerts.RemoveElementAt(i);
       break;
     }
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
@@ -533,21 +508,16 @@ OSXNotificationCenter::OnActivate(NSStri
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 void
 OSXNotificationCenter::ShowPendingNotification(OSXNotificationInfo *osxni)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  if (osxni->mIconTimeoutTimer) {
-    osxni->mIconTimeoutTimer->Cancel();
-    osxni->mIconTimeoutTimer = nullptr;
-  }
-
   if (osxni->mIconRequest) {
     osxni->mIconRequest->Cancel(NS_BINDING_ABORTED);
     osxni->mIconRequest = nullptr;
   }
 
   CloseAlertCocoaString(osxni->mName);
 
   for (unsigned int i = 0; i < mPendingAlerts.Length(); i++) {
@@ -566,67 +536,52 @@ OSXNotificationCenter::ShowPendingNotifi
 
   [osxni->mPendingNotifiction release];
   osxni->mPendingNotifiction = nil;
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 NS_IMETHODIMP
-OSXNotificationCenter::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
+OSXNotificationCenter::OnImageMissing(nsISupports* aUserData)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
-  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
-    OSXNotificationInfo *osxni = nullptr;
-    for (unsigned int i = 0; i < mPendingAlerts.Length(); i++) {
-      if (aRequest == mPendingAlerts[i]->mIconRequest) {
-        osxni = mPendingAlerts[i];
-        break;
-      }
-    }
-    if (!osxni || !osxni->mPendingNotifiction) {
-      return NS_ERROR_FAILURE;
-    }
-    NSImage *cocoaImage = nil;
-    uint32_t imgStatus = imgIRequest::STATUS_ERROR;
-    nsresult rv = aRequest->GetImageStatus(&imgStatus);
-    if (NS_SUCCEEDED(rv) && !(imgStatus & imgIRequest::STATUS_ERROR)) {
-      nsCOMPtr<imgIContainer> image;
-      rv = aRequest->GetImage(getter_AddRefs(image));
-      if (NS_SUCCEEDED(rv)) {
-        nsCocoaUtils::CreateNSImageFromImageContainer(image, imgIContainer::FRAME_FIRST, &cocoaImage, 1.0f);
-      }
-    }
-    (osxni->mPendingNotifiction).contentImage = cocoaImage;
-    [cocoaImage release];
+  OSXNotificationInfo *osxni = static_cast<OSXNotificationInfo*>(aUserData);
+  if (osxni->mPendingNotifiction) {
+    // If there was an error getting the image, or the request timed out, show
+    // the notification without a content image.
     ShowPendingNotification(osxni);
   }
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 NS_IMETHODIMP
-OSXNotificationCenter::Notify(nsITimer *timer)
+OSXNotificationCenter::OnImageReady(nsISupports* aUserData,
+                                    imgIRequest* aRequest)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
-  if (!timer) {
+  nsCOMPtr<imgIContainer> image;
+  nsresult rv = aRequest->GetImage(getter_AddRefs(image));
+  if (NS_WARN_IF(NS_FAILED(rv) || !image)) {
+    return rv;
+  }
+
+  OSXNotificationInfo *osxni = static_cast<OSXNotificationInfo*>(aUserData);
+  if (!osxni->mPendingNotifiction) {
     return NS_ERROR_FAILURE;
   }
 
-  for (unsigned int i = 0; i < mPendingAlerts.Length(); i++) {
-    OSXNotificationInfo *osxni = mPendingAlerts[i];
-    if (timer == osxni->mIconTimeoutTimer.get()) {
-      osxni->mIconTimeoutTimer = nullptr;
-      if (osxni->mPendingNotifiction) {
-        ShowPendingNotification(osxni);
-        break;
-      }
-    }
-  }
+  NSImage *cocoaImage = nil;
+  nsCocoaUtils::CreateNSImageFromImageContainer(image, imgIContainer::FRAME_FIRST, &cocoaImage, 1.0f);
+  (osxni->mPendingNotifiction).contentImage = cocoaImage;
+  [cocoaImage release];
+  ShowPendingNotification(osxni);
+
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 } // namespace mozilla