toolkit/system/gnome/nsAlertsIconListener.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/. */
     6 #include "nsAlertsIconListener.h"
     7 #include "imgIContainer.h"
     8 #include "imgILoader.h"
     9 #include "imgIRequest.h"
    10 #include "nsNetUtil.h"
    11 #include "nsIImageToPixbuf.h"
    12 #include "nsIStringBundle.h"
    13 #include "nsIObserverService.h"
    14 #include "nsCRT.h"
    16 #include <dlfcn.h>
    17 #include <gdk/gdk.h>
    19 static bool gHasActions = false;
    20 static bool gHasCaps = false;
    22 void* nsAlertsIconListener::libNotifyHandle = nullptr;
    23 bool nsAlertsIconListener::libNotifyNotAvail = false;
    24 nsAlertsIconListener::notify_is_initted_t nsAlertsIconListener::notify_is_initted = nullptr;
    25 nsAlertsIconListener::notify_init_t nsAlertsIconListener::notify_init = nullptr;
    26 nsAlertsIconListener::notify_get_server_caps_t nsAlertsIconListener::notify_get_server_caps = nullptr;
    27 nsAlertsIconListener::notify_notification_new_t nsAlertsIconListener::notify_notification_new = nullptr;
    28 nsAlertsIconListener::notify_notification_show_t nsAlertsIconListener::notify_notification_show = nullptr;
    29 nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr;
    30 nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr;
    32 static void notify_action_cb(NotifyNotification *notification,
    33                              gchar *action, gpointer user_data)
    34 {
    35   nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*> (user_data);
    36   alert->SendCallback();
    37 }
    39 static void notify_closed_marshal(GClosure* closure,
    40                                   GValue* return_value,
    41                                   guint n_param_values,
    42                                   const GValue* param_values,
    43                                   gpointer invocation_hint,
    44                                   gpointer marshal_data)
    45 {
    46   NS_ABORT_IF_FALSE(n_param_values >= 1, "No object in params");
    48   nsAlertsIconListener* alert =
    49     static_cast<nsAlertsIconListener*>(closure->data);
    50   alert->SendClosed();
    51   NS_RELEASE(alert);
    52 }
    54 NS_IMPL_ISUPPORTS(nsAlertsIconListener, imgINotificationObserver,
    55                   nsIObserver, nsISupportsWeakReference)
    57 nsAlertsIconListener::nsAlertsIconListener()
    58 : mLoadedFrame(false),
    59   mNotification(nullptr)
    60 {
    61   if (!libNotifyHandle && !libNotifyNotAvail) {
    62     libNotifyHandle = dlopen("libnotify.so.4", RTLD_LAZY);
    63     if (!libNotifyHandle) {
    64       libNotifyHandle = dlopen("libnotify.so.1", RTLD_LAZY);
    65       if (!libNotifyHandle) {
    66         libNotifyNotAvail = true;
    67         return;
    68       }
    69     }
    71     notify_is_initted = (notify_is_initted_t)dlsym(libNotifyHandle, "notify_is_initted");
    72     notify_init = (notify_init_t)dlsym(libNotifyHandle, "notify_init");
    73     notify_get_server_caps = (notify_get_server_caps_t)dlsym(libNotifyHandle, "notify_get_server_caps");
    74     notify_notification_new = (notify_notification_new_t)dlsym(libNotifyHandle, "notify_notification_new");
    75     notify_notification_show = (notify_notification_show_t)dlsym(libNotifyHandle, "notify_notification_show");
    76     notify_notification_set_icon_from_pixbuf = (notify_notification_set_icon_from_pixbuf_t)dlsym(libNotifyHandle, "notify_notification_set_icon_from_pixbuf");
    77     notify_notification_add_action = (notify_notification_add_action_t)dlsym(libNotifyHandle, "notify_notification_add_action");
    78     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) {
    79       dlclose(libNotifyHandle);
    80       libNotifyHandle = nullptr;
    81     }
    82   }
    83 }
    85 nsAlertsIconListener::~nsAlertsIconListener()
    86 {
    87   if (mIconRequest)
    88     mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
    89   // Don't dlclose libnotify as it uses atexit().
    90 }
    92 NS_IMETHODIMP
    93 nsAlertsIconListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
    94 {
    95   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
    96     return OnStopRequest(aRequest);
    97   }
    99   if (aType == imgINotificationObserver::FRAME_COMPLETE) {
   100     return OnStopFrame(aRequest);
   101   }
   103   return NS_OK;
   104 }
   106 nsresult
   107 nsAlertsIconListener::OnStopRequest(imgIRequest* aRequest)
   108 {
   109   uint32_t imgStatus = imgIRequest::STATUS_ERROR;
   110   nsresult rv = aRequest->GetImageStatus(&imgStatus);
   111   NS_ENSURE_SUCCESS(rv, rv);
   112   if (imgStatus == imgIRequest::STATUS_ERROR && !mLoadedFrame) {
   113     // We have an error getting the image. Display the notification with no icon.
   114     ShowAlert(nullptr);
   115   }
   117   if (mIconRequest) {
   118     mIconRequest->Cancel(NS_BINDING_ABORTED);
   119     mIconRequest = nullptr;
   120   }
   121   return NS_OK;
   122 }
   124 nsresult
   125 nsAlertsIconListener::OnStopFrame(imgIRequest* aRequest)
   126 {
   127   if (aRequest != mIconRequest)
   128     return NS_ERROR_FAILURE;
   130   if (mLoadedFrame)
   131     return NS_OK; // only use one frame
   133   nsCOMPtr<imgIContainer> image;
   134   nsresult rv = aRequest->GetImage(getter_AddRefs(image));
   135   if (NS_FAILED(rv))
   136     return rv;
   138   nsCOMPtr<nsIImageToPixbuf> imgToPixbuf =
   139     do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1");
   141   GdkPixbuf* imagePixbuf = imgToPixbuf->ConvertImageToPixbuf(image);
   142   if (!imagePixbuf)
   143     return NS_ERROR_FAILURE;
   145   ShowAlert(imagePixbuf);
   147   g_object_unref(imagePixbuf);
   149   mLoadedFrame = true;
   150   return NS_OK;
   151 }
   153 nsresult
   154 nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
   155 {
   156   mNotification = notify_notification_new(mAlertTitle.get(), mAlertText.get(),
   157                                           nullptr, nullptr);
   159   if (!mNotification)
   160     return NS_ERROR_OUT_OF_MEMORY;
   162   if (aPixbuf)
   163     notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf);
   165   NS_ADDREF(this);
   166   if (mAlertHasAction) {
   167     // What we put as the label doesn't matter here, if the action
   168     // string is "default" then that makes the entire bubble clickable
   169     // rather than creating a button.
   170     notify_notification_add_action(mNotification, "default", "Activate",
   171                                    notify_action_cb, this, nullptr);
   172   }
   174   // Fedora 10 calls NotifyNotification "closed" signal handlers with a
   175   // different signature, so a marshaller is used instead of a C callback to
   176   // get the user_data (this) in a parseable format.  |closure| is created
   177   // with a floating reference, which gets sunk by g_signal_connect_closure().
   178   GClosure* closure = g_closure_new_simple(sizeof(GClosure), this);
   179   g_closure_set_marshal(closure, notify_closed_marshal);
   180   mClosureHandler = g_signal_connect_closure(mNotification, "closed", closure, FALSE);
   181   gboolean result = notify_notification_show(mNotification, nullptr);
   183   return result ? NS_OK : NS_ERROR_FAILURE;
   184 }
   186 nsresult
   187 nsAlertsIconListener::StartRequest(const nsAString & aImageUrl)
   188 {
   189   if (mIconRequest) {
   190     // Another icon request is already in flight.  Kill it.
   191     mIconRequest->Cancel(NS_BINDING_ABORTED);
   192     mIconRequest = nullptr;
   193   }
   195   nsCOMPtr<nsIURI> imageUri;
   196   NS_NewURI(getter_AddRefs(imageUri), aImageUrl);
   197   if (!imageUri)
   198     return ShowAlert(nullptr);
   200   nsCOMPtr<imgILoader> il(do_GetService("@mozilla.org/image/loader;1"));
   201   if (!il)
   202     return ShowAlert(nullptr);
   204   // XXX: Hrmm.... Bypass cache, or isolate to imageUrl?
   205   return il->LoadImageXPCOM(imageUri, imageURI, nullptr, nullptr, nullptr,
   206                             this, nullptr, nsIRequest::LOAD_NORMAL, nullptr,
   207                             nullptr, getter_AddRefs(mIconRequest));
   208 }
   210 void
   211 nsAlertsIconListener::SendCallback()
   212 {
   213   if (mAlertListener)
   214     mAlertListener->Observe(nullptr, "alertclickcallback", mAlertCookie.get());
   215 }
   217 void
   218 nsAlertsIconListener::SendClosed()
   219 {
   220   if (mNotification) {
   221     g_object_unref(mNotification);
   222     mNotification = nullptr;
   223   }
   224   if (mAlertListener)
   225     mAlertListener->Observe(nullptr, "alertfinished", mAlertCookie.get());
   226 }
   228 NS_IMETHODIMP
   229 nsAlertsIconListener::Observe(nsISupports *aSubject, const char *aTopic,
   230                               const char16_t *aData) {
   231   // We need to close any open notifications upon application exit, otherwise
   232   // we will leak since libnotify holds a ref for us.
   233   if (!nsCRT::strcmp(aTopic, "quit-application") && mNotification) {
   234     g_signal_handler_disconnect(mNotification, mClosureHandler);
   235     g_object_unref(mNotification);
   236     mNotification = nullptr;
   237     Release(); // equivalent to NS_RELEASE(this)
   238   }
   239   return NS_OK;
   240 }
   242 nsresult
   243 nsAlertsIconListener::InitAlertAsync(const nsAString & aImageUrl,
   244                                      const nsAString & aAlertTitle, 
   245                                      const nsAString & aAlertText,
   246                                      bool aAlertTextClickable,
   247                                      const nsAString & aAlertCookie,
   248                                      nsIObserver * aAlertListener)
   249 {
   250   if (!libNotifyHandle)
   251     return NS_ERROR_FAILURE;
   253   if (!notify_is_initted()) {
   254     // Give the name of this application to libnotify
   255     nsCOMPtr<nsIStringBundleService> bundleService = 
   256       do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   258     nsAutoCString appShortName;
   259     if (bundleService) {
   260       nsCOMPtr<nsIStringBundle> bundle;
   261       bundleService->CreateBundle("chrome://branding/locale/brand.properties",
   262                                   getter_AddRefs(bundle));
   263       nsAutoString appName;
   265       if (bundle) {
   266         bundle->GetStringFromName(MOZ_UTF16("brandShortName"),
   267                                   getter_Copies(appName));
   268         appShortName = NS_ConvertUTF16toUTF8(appName);
   269       } else {
   270         NS_WARNING("brand.properties not present, using default application name");
   271         appShortName.AssignLiteral("Mozilla");
   272       }
   273     } else {
   274       appShortName.AssignLiteral("Mozilla");
   275     }
   277     if (!notify_init(appShortName.get()))
   278       return NS_ERROR_FAILURE;
   280     GList *server_caps = notify_get_server_caps();
   281     if (server_caps) {
   282       gHasCaps = true;
   283       for (GList* cap = server_caps; cap != nullptr; cap = cap->next) {
   284         if (!strcmp((char*) cap->data, "actions")) {
   285           gHasActions = true;
   286           break;
   287         }
   288       }
   289       g_list_foreach(server_caps, (GFunc)g_free, nullptr);
   290       g_list_free(server_caps);
   291     }
   292   }
   294   if (!gHasCaps) {
   295     // if notify_get_server_caps() failed above we need to assume
   296     // there is no notification-server to display anything
   297     return NS_ERROR_FAILURE;
   298   }
   300   if (!gHasActions && aAlertTextClickable)
   301     return NS_ERROR_FAILURE; // No good, fallback to XUL
   303   nsCOMPtr<nsIObserverService> obsServ =
   304       do_GetService("@mozilla.org/observer-service;1");
   305   if (obsServ)
   306     obsServ->AddObserver(this, "quit-application", true);
   308   // Workaround for a libnotify bug - blank titles aren't dealt with
   309   // properly so we use a space
   310   if (aAlertTitle.IsEmpty()) {
   311     mAlertTitle = NS_LITERAL_CSTRING(" ");
   312   } else {
   313     mAlertTitle = NS_ConvertUTF16toUTF8(aAlertTitle);
   314   }
   316   mAlertText = NS_ConvertUTF16toUTF8(aAlertText);
   317   mAlertHasAction = aAlertTextClickable;
   319   mAlertListener = aAlertListener;
   320   mAlertCookie = aAlertCookie;
   322   return StartRequest(aImageUrl);
   323 }

mercurial