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.

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

mercurial