Bug 529664 - sporadic 136-144 byte leak of nsAlertsIconListener + nsStringBuffer on Linux, r=roc
authorMichael Ventnor <ventnor.bugzilla@gmail.com>
Tue, 01 Dec 2009 19:39:00 -0800
changeset 35711 efa4edae64d961bf831cbb6903868f4dc82911e4
parent 35710 8f6bdbf8e7017fb0bdfe875035402c60701a8cdb
child 35712 f1742529aaa793c487e6a2e67664d3d1a9272701
push id10688
push userphilringnalda@gmail.com
push dateMon, 14 Dec 2009 05:28:09 +0000
treeherdermozilla-central@efa4edae64d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs529664
milestone1.9.3a1pre
Bug 529664 - sporadic 136-144 byte leak of nsAlertsIconListener + nsStringBuffer on Linux, r=roc
toolkit/system/gnome/nsAlertsIconListener.cpp
toolkit/system/gnome/nsAlertsIconListener.h
toolkit/system/gnome/nsAlertsService.cpp
--- a/toolkit/system/gnome/nsAlertsIconListener.cpp
+++ b/toolkit/system/gnome/nsAlertsIconListener.cpp
@@ -37,19 +37,18 @@
 
 #include "nsAlertsIconListener.h"
 #include "imgIContainer.h"
 #include "imgILoader.h"
 #include "imgIRequest.h"
 #include "nsNetUtil.h"
 #include "nsIImageToPixbuf.h"
 #include "nsIStringBundle.h"
+#include "nsIObserverService.h"
 
-#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <libnotify/notify.h>
 #include <gdk/gdk.h>
 
 static PRBool gHasActions = PR_FALSE;
 
 static void notify_action_cb(NotifyNotification *notification,
                              gchar *action, gpointer user_data)
 {
   nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*> (user_data);
@@ -69,30 +68,42 @@ static void notify_closed_marshal(GClosu
   g_object_unref(notification);
 
   nsAlertsIconListener* alert =
     static_cast<nsAlertsIconListener*>(closure->data);
   alert->SendClosed();
   NS_RELEASE(alert);
 }
 
-NS_IMPL_ISUPPORTS2(nsAlertsIconListener, imgIContainerObserver, imgIDecoderObserver)
+NS_IMPL_ISUPPORTS3(nsAlertsIconListener, imgIContainerObserver, imgIDecoderObserver, nsIObserver)
 
 nsAlertsIconListener::nsAlertsIconListener()
-: mLoadedFrame(PR_FALSE)
+: mLoadedFrame(PR_FALSE),
+  mHasQuit(PR_FALSE),
+  mNotification(NULL)
 {
   MOZ_COUNT_CTOR(nsAlertsIconListener);
+
+  nsCOMPtr<nsIObserverService> obsServ =
+      do_GetService("@mozilla.org/observer-service;1");
+  obsServ->AddObserver(this, "quit-application", PR_FALSE);
 }
 
 nsAlertsIconListener::~nsAlertsIconListener()
 {
   MOZ_COUNT_DTOR(nsAlertsIconListener);
 
   if (mIconRequest)
     mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
+
+  if (!mHasQuit) {
+    nsCOMPtr<nsIObserverService> obsServ =
+        do_GetService("@mozilla.org/observer-service;1");
+    obsServ->RemoveObserver(this, "quit-application");
+  }
 }
 
 NS_IMETHODIMP
 nsAlertsIconListener::OnStartRequest(imgIRequest* aRequest)
 {
   return NS_OK;
 }
 
@@ -203,42 +214,42 @@ nsAlertsIconListener::OnStopFrame(imgIRe
 
   mLoadedFrame = PR_TRUE;
   return NS_OK;
 }
 
 nsresult
 nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
 {
-  NotifyNotification* notify = notify_notification_new(mAlertTitle.get(),
-                                                       mAlertText.get(),
-                                                       NULL, NULL);
-  if (!notify)
+  mNotification = notify_notification_new(mAlertTitle.get(),
+                                          mAlertText.get(),
+                                          NULL, NULL);
+  if (!mNotification)
     return NS_ERROR_OUT_OF_MEMORY;
 
   if (aPixbuf)
-    notify_notification_set_icon_from_pixbuf(notify, aPixbuf);
+    notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf);
 
   NS_ADDREF(this);
   if (mAlertHasAction) {
     // What we put as the label doesn't matter here, if the action
     // string is "default" then that makes the entire bubble clickable
     // rather than creating a button.
-    notify_notification_add_action(notify, "default", "Activate",
+    notify_notification_add_action(mNotification, "default", "Activate",
                                    notify_action_cb, this, NULL);
   }
 
   // Fedora 10 calls NotifyNotification "closed" signal handlers with a
   // different signature, so a marshaller is used instead of a C callback to
   // get the user_data (this) in a parseable format.  |closure| is created
   // with a floating reference, which gets sunk by g_signal_connect_closure().
   GClosure* closure = g_closure_new_simple(sizeof(GClosure), this);
   g_closure_set_marshal(closure, notify_closed_marshal);
-  g_signal_connect_closure(notify, "closed", closure, FALSE);
-  gboolean result = notify_notification_show(notify, NULL);
+  g_signal_connect_closure(mNotification, "closed", closure, FALSE);
+  gboolean result = notify_notification_show(mNotification, NULL);
 
   return result ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 nsAlertsIconListener::StartRequest(const nsAString & aImageUrl)
 {
   if (mIconRequest) {
@@ -266,20 +277,33 @@ nsAlertsIconListener::SendCallback()
 {
   if (mAlertListener)
     mAlertListener->Observe(NULL, "alertclickcallback", mAlertCookie.get());
 }
 
 void
 nsAlertsIconListener::SendClosed()
 {
+  mNotification = NULL;
   if (mAlertListener)
     mAlertListener->Observe(NULL, "alertfinished", mAlertCookie.get());
 }
 
+NS_IMETHODIMP
+nsAlertsIconListener::Observe(nsISupports *aSubject, const char *aTopic,
+                              const PRUnichar *aData) {
+  // We need to close any open notifications upon application exit, otherwise
+  // we will leak since libnotify holds a ref for us.
+  if (!nsCRT::strcmp(aTopic, "quit-application") && mNotification) {
+    notify_notification_close(mNotification, NULL);
+    mHasQuit = PR_TRUE;
+  }
+  return NS_OK;
+}
+
 nsresult
 nsAlertsIconListener::InitAlertAsync(const nsAString & aImageUrl,
                                      const nsAString & aAlertTitle, 
                                      const nsAString & aAlertText,
                                      PRBool aAlertTextClickable,
                                      const nsAString & aAlertCookie,
                                      nsIObserver * aAlertListener)
 {
--- a/toolkit/system/gnome/nsAlertsIconListener.h
+++ b/toolkit/system/gnome/nsAlertsIconListener.h
@@ -39,25 +39,28 @@
 #define nsAlertsIconListener_h__
 
 #include "nsCOMPtr.h"
 #include "imgIDecoderObserver.h"
 #include "nsStringAPI.h"
 #include "nsIObserver.h"
 
 #include <gdk-pixbuf/gdk-pixbuf.h>
+#include <libnotify/notify.h>
 
 class imgIRequest;
 
-class nsAlertsIconListener : public imgIDecoderObserver
+class nsAlertsIconListener : public imgIDecoderObserver,
+                             public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGICONTAINEROBSERVER
   NS_DECL_IMGIDECODEROBSERVER
+  NS_DECL_NSIOBSERVER
 
   nsAlertsIconListener();
   virtual ~nsAlertsIconListener();
 
   nsresult InitAlertAsync(const nsAString & aImageUrl,
                           const nsAString & aAlertTitle, 
                           const nsAString & aAlertText,
                           PRBool aAlertTextClickable,
@@ -72,14 +75,17 @@ protected:
   nsCString mAlertTitle;
   nsCString mAlertText;
 
   nsCOMPtr<nsIObserver> mAlertListener;
   nsString mAlertCookie;
 
   PRPackedBool mLoadedFrame;
   PRPackedBool mAlertHasAction;
+  PRPackedBool mHasQuit;
+
+  NotifyNotification* mNotification;
 
   nsresult StartRequest(const nsAString & aImageUrl);
   nsresult ShowAlert(GdkPixbuf* aPixbuf);
 };
 
 #endif
--- a/toolkit/system/gnome/nsAlertsService.cpp
+++ b/toolkit/system/gnome/nsAlertsService.cpp
@@ -32,16 +32,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsAlertsService.h"
 #include "nsAlertsIconListener.h"
+#include "nsAutoPtr.h"
 
 NS_IMPL_THREADSAFE_ADDREF(nsAlertsService)
 NS_IMPL_THREADSAFE_RELEASE(nsAlertsService)
 
 NS_INTERFACE_MAP_BEGIN(nsAlertsService)
    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAlertsService)
    NS_INTERFACE_MAP_ENTRY(nsIAlertsService)
 NS_INTERFACE_MAP_END_THREADSAFE
@@ -59,15 +60,15 @@ nsAlertsService::Init()
 }
 
 NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle, 
                                                      const nsAString & aAlertText, PRBool aAlertTextClickable,
                                                      const nsAString & aAlertCookie,
                                                      nsIObserver * aAlertListener,
                                                      const nsAString & aAlertName)
 {
-  nsCOMPtr<nsAlertsIconListener> alertListener = new nsAlertsIconListener();
+  nsRefPtr<nsAlertsIconListener> alertListener = new nsAlertsIconListener();
   if (!alertListener)
     return NS_ERROR_OUT_OF_MEMORY;
 
   return alertListener->InitAlertAsync(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable,
                                        aAlertCookie, aAlertListener);
 }