LCOV - code coverage report
Current view: top level - toolkit/system/gnome - nsAlertsIconListener.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 161 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 18 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "nsAlertsIconListener.h"
       7             : #include "imgIContainer.h"
       8             : #include "imgIRequest.h"
       9             : #include "nsNetUtil.h"
      10             : #include "nsServiceManagerUtils.h"
      11             : #include "nsSystemAlertsService.h"
      12             : #include "nsIAlertsService.h"
      13             : #include "nsICancelable.h"
      14             : #include "nsIImageToPixbuf.h"
      15             : #include "nsIStringBundle.h"
      16             : #include "nsIObserverService.h"
      17             : #include "nsIURI.h"
      18             : #include "nsCRT.h"
      19             : 
      20             : #include <dlfcn.h>
      21             : #include <gdk/gdk.h>
      22             : 
      23             : static bool gHasActions = false;
      24             : static bool gHasCaps = false;
      25             : 
      26             : void* nsAlertsIconListener::libNotifyHandle = nullptr;
      27             : bool nsAlertsIconListener::libNotifyNotAvail = false;
      28             : nsAlertsIconListener::notify_is_initted_t nsAlertsIconListener::notify_is_initted = nullptr;
      29             : nsAlertsIconListener::notify_init_t nsAlertsIconListener::notify_init = nullptr;
      30             : nsAlertsIconListener::notify_get_server_caps_t nsAlertsIconListener::notify_get_server_caps = nullptr;
      31             : nsAlertsIconListener::notify_notification_new_t nsAlertsIconListener::notify_notification_new = nullptr;
      32             : nsAlertsIconListener::notify_notification_show_t nsAlertsIconListener::notify_notification_show = nullptr;
      33             : nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr;
      34             : nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr;
      35             : nsAlertsIconListener::notify_notification_close_t nsAlertsIconListener::notify_notification_close = nullptr;
      36             : 
      37           0 : static void notify_action_cb(NotifyNotification *notification,
      38             :                              gchar *action, gpointer user_data)
      39             : {
      40           0 :   nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*> (user_data);
      41           0 :   alert->SendCallback();
      42           0 : }
      43             : 
      44           0 : static void notify_closed_marshal(GClosure* closure,
      45             :                                   GValue* return_value,
      46             :                                   guint n_param_values,
      47             :                                   const GValue* param_values,
      48             :                                   gpointer invocation_hint,
      49             :                                   gpointer marshal_data)
      50             : {
      51           0 :   MOZ_ASSERT(n_param_values >= 1, "No object in params");
      52             : 
      53             :   nsAlertsIconListener* alert =
      54           0 :     static_cast<nsAlertsIconListener*>(closure->data);
      55           0 :   alert->SendClosed();
      56           0 :   NS_RELEASE(alert);
      57           0 : }
      58             : 
      59             : static GdkPixbuf*
      60           0 : GetPixbufFromImgRequest(imgIRequest* aRequest)
      61             : {
      62           0 :   nsCOMPtr<imgIContainer> image;
      63           0 :   nsresult rv = aRequest->GetImage(getter_AddRefs(image));
      64           0 :   if (NS_FAILED(rv)) {
      65           0 :     return nullptr;
      66             :   }
      67             : 
      68             :   nsCOMPtr<nsIImageToPixbuf> imgToPixbuf =
      69           0 :     do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1");
      70             : 
      71           0 :   return imgToPixbuf->ConvertImageToPixbuf(image);
      72             : }
      73             : 
      74           0 : NS_IMPL_ISUPPORTS(nsAlertsIconListener, nsIAlertNotificationImageListener,
      75             :                   nsIObserver, nsISupportsWeakReference)
      76             : 
      77           0 : nsAlertsIconListener::nsAlertsIconListener(nsSystemAlertsService* aBackend,
      78           0 :                                            const nsAString& aAlertName)
      79             : : mAlertName(aAlertName),
      80             :   mBackend(aBackend),
      81           0 :   mNotification(nullptr)
      82             : {
      83           0 :   if (!libNotifyHandle && !libNotifyNotAvail) {
      84           0 :     libNotifyHandle = dlopen("libnotify.so.4", RTLD_LAZY);
      85           0 :     if (!libNotifyHandle) {
      86           0 :       libNotifyHandle = dlopen("libnotify.so.1", RTLD_LAZY);
      87           0 :       if (!libNotifyHandle) {
      88           0 :         libNotifyNotAvail = true;
      89           0 :         return;
      90             :       }
      91             :     }
      92             : 
      93           0 :     notify_is_initted = (notify_is_initted_t)dlsym(libNotifyHandle, "notify_is_initted");
      94           0 :     notify_init = (notify_init_t)dlsym(libNotifyHandle, "notify_init");
      95           0 :     notify_get_server_caps = (notify_get_server_caps_t)dlsym(libNotifyHandle, "notify_get_server_caps");
      96           0 :     notify_notification_new = (notify_notification_new_t)dlsym(libNotifyHandle, "notify_notification_new");
      97           0 :     notify_notification_show = (notify_notification_show_t)dlsym(libNotifyHandle, "notify_notification_show");
      98           0 :     notify_notification_set_icon_from_pixbuf = (notify_notification_set_icon_from_pixbuf_t)dlsym(libNotifyHandle, "notify_notification_set_icon_from_pixbuf");
      99           0 :     notify_notification_add_action = (notify_notification_add_action_t)dlsym(libNotifyHandle, "notify_notification_add_action");
     100           0 :     notify_notification_close = (notify_notification_close_t)dlsym(libNotifyHandle, "notify_notification_close");
     101           0 :     if (!notify_is_initted || !notify_init || !notify_get_server_caps || !notify_notification_new || !notify_notification_show || !notify_notification_set_icon_from_pixbuf || !notify_notification_add_action || !notify_notification_close) {
     102           0 :       dlclose(libNotifyHandle);
     103           0 :       libNotifyHandle = nullptr;
     104             :     }
     105             :   }
     106             : }
     107             : 
     108           0 : nsAlertsIconListener::~nsAlertsIconListener()
     109             : {
     110           0 :   mBackend->RemoveListener(mAlertName, this);
     111             :   // Don't dlclose libnotify as it uses atexit().
     112           0 : }
     113             : 
     114             : NS_IMETHODIMP
     115           0 : nsAlertsIconListener::OnImageMissing(nsISupports*)
     116             : {
     117             :   // This notification doesn't have an image, or there was an error getting
     118             :   // the image. Show the notification without an icon.
     119           0 :   return ShowAlert(nullptr);
     120             : }
     121             : 
     122             : NS_IMETHODIMP
     123           0 : nsAlertsIconListener::OnImageReady(nsISupports*, imgIRequest* aRequest)
     124             : {
     125           0 :   GdkPixbuf* imagePixbuf = GetPixbufFromImgRequest(aRequest);
     126           0 :   if (!imagePixbuf) {
     127           0 :     ShowAlert(nullptr);
     128             :   } else {
     129           0 :     ShowAlert(imagePixbuf);
     130           0 :     g_object_unref(imagePixbuf);
     131             :   }
     132             : 
     133           0 :   return NS_OK;
     134             : }
     135             : 
     136             : nsresult
     137           0 : nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
     138             : {
     139           0 :   if (!mBackend->IsActiveListener(mAlertName, this))
     140           0 :     return NS_OK;
     141             : 
     142           0 :   mNotification = notify_notification_new(mAlertTitle.get(), mAlertText.get(),
     143             :                                           nullptr, nullptr);
     144             : 
     145           0 :   if (!mNotification)
     146           0 :     return NS_ERROR_OUT_OF_MEMORY;
     147             : 
     148             :   nsCOMPtr<nsIObserverService> obsServ =
     149           0 :       do_GetService("@mozilla.org/observer-service;1");
     150           0 :   if (obsServ)
     151           0 :     obsServ->AddObserver(this, "quit-application", true);
     152             : 
     153           0 :   if (aPixbuf)
     154           0 :     notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf);
     155             : 
     156           0 :   NS_ADDREF(this);
     157           0 :   if (mAlertHasAction) {
     158             :     // What we put as the label doesn't matter here, if the action
     159             :     // string is "default" then that makes the entire bubble clickable
     160             :     // rather than creating a button.
     161           0 :     notify_notification_add_action(mNotification, "default", "Activate",
     162           0 :                                    notify_action_cb, this, nullptr);
     163             :   }
     164             : 
     165             :   // Fedora 10 calls NotifyNotification "closed" signal handlers with a
     166             :   // different signature, so a marshaller is used instead of a C callback to
     167             :   // get the user_data (this) in a parseable format.  |closure| is created
     168             :   // with a floating reference, which gets sunk by g_signal_connect_closure().
     169           0 :   GClosure* closure = g_closure_new_simple(sizeof(GClosure), this);
     170           0 :   g_closure_set_marshal(closure, notify_closed_marshal);
     171           0 :   mClosureHandler = g_signal_connect_closure(mNotification, "closed", closure, FALSE);
     172           0 :   GError* error = nullptr;
     173           0 :   if (!notify_notification_show(mNotification, &error)) {
     174           0 :     NS_WARNING(error->message);
     175           0 :     g_error_free(error);
     176           0 :     return NS_ERROR_FAILURE;
     177             :   }
     178             : 
     179           0 :   if (mAlertListener)
     180           0 :     mAlertListener->Observe(nullptr, "alertshow", mAlertCookie.get());
     181             : 
     182           0 :   return NS_OK;
     183             : }
     184             : 
     185             : void
     186           0 : nsAlertsIconListener::SendCallback()
     187             : {
     188           0 :   if (mAlertListener)
     189           0 :     mAlertListener->Observe(nullptr, "alertclickcallback", mAlertCookie.get());
     190           0 : }
     191             : 
     192             : void
     193           0 : nsAlertsIconListener::SendClosed()
     194             : {
     195           0 :   if (mNotification) {
     196           0 :     g_object_unref(mNotification);
     197           0 :     mNotification = nullptr;
     198             :   }
     199           0 :   NotifyFinished();
     200           0 : }
     201             : 
     202             : NS_IMETHODIMP
     203           0 : nsAlertsIconListener::Observe(nsISupports *aSubject, const char *aTopic,
     204             :                               const char16_t *aData) {
     205             :   // We need to close any open notifications upon application exit, otherwise
     206             :   // we will leak since libnotify holds a ref for us.
     207           0 :   if (!nsCRT::strcmp(aTopic, "quit-application") && mNotification) {
     208           0 :     g_signal_handler_disconnect(mNotification, mClosureHandler);
     209           0 :     g_object_unref(mNotification);
     210           0 :     mNotification = nullptr;
     211           0 :     Release(); // equivalent to NS_RELEASE(this)
     212             :   }
     213           0 :   return NS_OK;
     214             : }
     215             : 
     216             : nsresult
     217           0 : nsAlertsIconListener::Close()
     218             : {
     219           0 :   if (mIconRequest) {
     220           0 :     mIconRequest->Cancel(NS_BINDING_ABORTED);
     221           0 :     mIconRequest = nullptr;
     222             :   }
     223             : 
     224           0 :   if (!mNotification) {
     225           0 :     NotifyFinished();
     226           0 :     return NS_OK;
     227             :   }
     228             : 
     229           0 :   GError* error = nullptr;
     230           0 :   if (!notify_notification_close(mNotification, &error)) {
     231           0 :     NS_WARNING(error->message);
     232           0 :     g_error_free(error);
     233           0 :     return NS_ERROR_FAILURE;
     234             :   }
     235             : 
     236           0 :   return NS_OK;
     237             : }
     238             : 
     239             : nsresult
     240           0 : nsAlertsIconListener::InitAlertAsync(nsIAlertNotification* aAlert,
     241             :                                      nsIObserver* aAlertListener)
     242             : {
     243           0 :   if (!libNotifyHandle)
     244           0 :     return NS_ERROR_FAILURE;
     245             : 
     246           0 :   if (!notify_is_initted()) {
     247             :     // Give the name of this application to libnotify
     248             :     nsCOMPtr<nsIStringBundleService> bundleService =
     249           0 :       do_GetService(NS_STRINGBUNDLE_CONTRACTID);
     250             : 
     251           0 :     nsAutoCString appShortName;
     252           0 :     if (bundleService) {
     253           0 :       nsCOMPtr<nsIStringBundle> bundle;
     254           0 :       bundleService->CreateBundle("chrome://branding/locale/brand.properties",
     255           0 :                                   getter_AddRefs(bundle));
     256           0 :       nsAutoString appName;
     257             : 
     258           0 :       if (bundle) {
     259           0 :         bundle->GetStringFromName(u"brandShortName",
     260           0 :                                   getter_Copies(appName));
     261           0 :         appShortName = NS_ConvertUTF16toUTF8(appName);
     262             :       } else {
     263           0 :         NS_WARNING("brand.properties not present, using default application name");
     264           0 :         appShortName.AssignLiteral("Mozilla");
     265             :       }
     266             :     } else {
     267           0 :       appShortName.AssignLiteral("Mozilla");
     268             :     }
     269             : 
     270           0 :     if (!notify_init(appShortName.get()))
     271           0 :       return NS_ERROR_FAILURE;
     272             : 
     273           0 :     GList *server_caps = notify_get_server_caps();
     274           0 :     if (server_caps) {
     275           0 :       gHasCaps = true;
     276           0 :       for (GList* cap = server_caps; cap != nullptr; cap = cap->next) {
     277           0 :         if (!strcmp((char*) cap->data, "actions")) {
     278           0 :           gHasActions = true;
     279           0 :           break;
     280             :         }
     281             :       }
     282           0 :       g_list_foreach(server_caps, (GFunc)g_free, nullptr);
     283           0 :       g_list_free(server_caps);
     284             :     }
     285             :   }
     286             : 
     287           0 :   if (!gHasCaps) {
     288             :     // if notify_get_server_caps() failed above we need to assume
     289             :     // there is no notification-server to display anything
     290           0 :     return NS_ERROR_FAILURE;
     291             :   }
     292             : 
     293           0 :   nsresult rv = aAlert->GetTextClickable(&mAlertHasAction);
     294           0 :   NS_ENSURE_SUCCESS(rv, rv);
     295           0 :   if (!gHasActions && mAlertHasAction)
     296           0 :     return NS_ERROR_FAILURE; // No good, fallback to XUL
     297             : 
     298           0 :   nsAutoString title;
     299           0 :   rv = aAlert->GetTitle(title);
     300           0 :   NS_ENSURE_SUCCESS(rv, rv);
     301             :   // Workaround for a libnotify bug - blank titles aren't dealt with
     302             :   // properly so we use a space
     303           0 :   if (title.IsEmpty()) {
     304           0 :     mAlertTitle = NS_LITERAL_CSTRING(" ");
     305             :   } else {
     306           0 :     mAlertTitle = NS_ConvertUTF16toUTF8(title);
     307             :   }
     308             : 
     309           0 :   nsAutoString text;
     310           0 :   rv = aAlert->GetText(text);
     311           0 :   NS_ENSURE_SUCCESS(rv, rv);
     312           0 :   mAlertText = NS_ConvertUTF16toUTF8(text);
     313             : 
     314           0 :   mAlertListener = aAlertListener;
     315             : 
     316           0 :   rv = aAlert->GetCookie(mAlertCookie);
     317           0 :   NS_ENSURE_SUCCESS(rv, rv);
     318             : 
     319           0 :   return aAlert->LoadImage(/* aTimeout = */ 0, this, /* aUserData = */ nullptr,
     320           0 :                            getter_AddRefs(mIconRequest));
     321             : }
     322             : 
     323           0 : void nsAlertsIconListener::NotifyFinished()
     324             : {
     325           0 :   if (mAlertListener)
     326           0 :     mAlertListener->Observe(nullptr, "alertfinished", mAlertCookie.get());
     327           0 : }

Generated by: LCOV version 1.13