1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2008
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsAlertsIconListener.h"
39 #include "imgIContainer.h"
40 #include "imgILoader.h"
41 #include "imgIRequest.h"
42 #include "nsNetUtil.h"
43 #include "nsIImageToPixbuf.h"
44 #include "nsIStringBundle.h"
45 #include "nsIObserverService.h"
49 // Compatibility macro for <libnotify-0.7
50 #ifndef NOTIFY_CHECK_VERSION
51 #define NOTIFY_CHECK_VERSION(x,y,z) 0
54 static PRBool gHasActions
= PR_FALSE
;
56 static void notify_action_cb(NotifyNotification
*notification
,
57 gchar
*action
, gpointer user_data
)
59 nsAlertsIconListener
* alert
= static_cast<nsAlertsIconListener
*> (user_data
);
60 alert
->SendCallback();
63 static void notify_closed_marshal(GClosure
* closure
,
66 const GValue
* param_values
,
67 gpointer invocation_hint
,
68 gpointer marshal_data
)
70 NS_ABORT_IF_FALSE(n_param_values
>= 1, "No object in params");
72 nsAlertsIconListener
* alert
=
73 static_cast<nsAlertsIconListener
*>(closure
->data
);
78 NS_IMPL_ISUPPORTS3(nsAlertsIconListener
, imgIContainerObserver
, imgIDecoderObserver
, nsIObserver
)
80 nsAlertsIconListener::nsAlertsIconListener()
81 : mLoadedFrame(PR_FALSE
),
85 MOZ_COUNT_CTOR(nsAlertsIconListener
);
87 nsCOMPtr
<nsIObserverService
> obsServ
=
88 do_GetService("@mozilla.org/observer-service;1");
89 obsServ
->AddObserver(this, "quit-application", PR_FALSE
);
92 nsAlertsIconListener::~nsAlertsIconListener()
94 MOZ_COUNT_DTOR(nsAlertsIconListener
);
97 mIconRequest
->CancelAndForgetObserver(NS_BINDING_ABORTED
);
100 nsCOMPtr
<nsIObserverService
> obsServ
=
101 do_GetService("@mozilla.org/observer-service;1");
102 obsServ
->RemoveObserver(this, "quit-application");
107 nsAlertsIconListener::OnStartRequest(imgIRequest
* aRequest
)
113 nsAlertsIconListener::OnStartDecode(imgIRequest
* aRequest
)
120 nsAlertsIconListener::OnStartContainer(imgIRequest
* aRequest
,
121 imgIContainer
* aContainer
)
128 nsAlertsIconListener::OnStartFrame(imgIRequest
* aRequest
,
136 nsAlertsIconListener::OnDataAvailable(imgIRequest
* aRequest
,
137 PRBool aCurrentFrame
,
138 const nsIntRect
* aRect
)
144 nsAlertsIconListener::OnStopContainer(imgIRequest
* aRequest
,
145 imgIContainer
* aContainer
)
152 nsAlertsIconListener::OnStopDecode(imgIRequest
* aRequest
,
154 const PRUnichar
* statusArg
)
160 nsAlertsIconListener::FrameChanged(imgIContainer
* aContainer
,
161 const nsIntRect
* aDirtyRect
)
167 nsAlertsIconListener::OnStopRequest(imgIRequest
* aRequest
,
170 PRUint32 imgStatus
= imgIRequest::STATUS_ERROR
;
171 nsresult rv
= aRequest
->GetImageStatus(&imgStatus
);
172 NS_ENSURE_SUCCESS(rv
, rv
);
173 if (imgStatus
== imgIRequest::STATUS_ERROR
&& !mLoadedFrame
) {
174 // We have an error getting the image. Display the notification with no icon.
179 mIconRequest
->Cancel(NS_BINDING_ABORTED
);
180 mIconRequest
= nsnull
;
186 nsAlertsIconListener::OnDiscard(imgIRequest
*aRequest
)
192 nsAlertsIconListener::OnStopFrame(imgIRequest
* aRequest
,
195 if (aRequest
!= mIconRequest
)
196 return NS_ERROR_FAILURE
;
199 return NS_OK
; // only use one frame
201 nsCOMPtr
<imgIContainer
> image
;
202 nsresult rv
= aRequest
->GetImage(getter_AddRefs(image
));
206 nsCOMPtr
<nsIImageToPixbuf
> imgToPixbuf
=
207 do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1");
209 GdkPixbuf
* imagePixbuf
= imgToPixbuf
->ConvertImageToPixbuf(image
);
211 return NS_ERROR_FAILURE
;
213 ShowAlert(imagePixbuf
);
215 g_object_unref(imagePixbuf
);
217 mLoadedFrame
= PR_TRUE
;
222 nsAlertsIconListener::ShowAlert(GdkPixbuf
* aPixbuf
)
224 mNotification
= notify_notification_new(mAlertTitle
.get(),
227 // >=libnotify-0.7.0 has no support for attaching to widgets
228 #if !NOTIFY_CHECK_VERSION(0,7,0)
234 return NS_ERROR_OUT_OF_MEMORY
;
237 notify_notification_set_icon_from_pixbuf(mNotification
, aPixbuf
);
240 if (mAlertHasAction
) {
241 // What we put as the label doesn't matter here, if the action
242 // string is "default" then that makes the entire bubble clickable
243 // rather than creating a button.
244 notify_notification_add_action(mNotification
, "default", "Activate",
245 notify_action_cb
, this, NULL
);
248 // Fedora 10 calls NotifyNotification "closed" signal handlers with a
249 // different signature, so a marshaller is used instead of a C callback to
250 // get the user_data (this) in a parseable format. |closure| is created
251 // with a floating reference, which gets sunk by g_signal_connect_closure().
252 GClosure
* closure
= g_closure_new_simple(sizeof(GClosure
), this);
253 g_closure_set_marshal(closure
, notify_closed_marshal
);
254 mClosureHandler
= g_signal_connect_closure(mNotification
, "closed", closure
, FALSE
);
255 gboolean result
= notify_notification_show(mNotification
, NULL
);
257 return result
? NS_OK
: NS_ERROR_FAILURE
;
261 nsAlertsIconListener::StartRequest(const nsAString
& aImageUrl
)
264 // Another icon request is already in flight. Kill it.
265 mIconRequest
->Cancel(NS_BINDING_ABORTED
);
266 mIconRequest
= nsnull
;
269 nsCOMPtr
<nsIURI
> imageUri
;
270 NS_NewURI(getter_AddRefs(imageUri
), aImageUrl
);
272 return ShowAlert(NULL
);
274 nsCOMPtr
<imgILoader
> il(do_GetService("@mozilla.org/image/loader;1"));
276 return ShowAlert(NULL
);
278 return il
->LoadImage(imageUri
, nsnull
, nsnull
, nsnull
, this,
279 nsnull
, nsIRequest::LOAD_NORMAL
, nsnull
, nsnull
,
280 nsnull
, getter_AddRefs(mIconRequest
));
284 nsAlertsIconListener::SendCallback()
287 mAlertListener
->Observe(NULL
, "alertclickcallback", mAlertCookie
.get());
291 nsAlertsIconListener::SendClosed()
294 g_object_unref(mNotification
);
295 mNotification
= NULL
;
298 mAlertListener
->Observe(NULL
, "alertfinished", mAlertCookie
.get());
302 nsAlertsIconListener::Observe(nsISupports
*aSubject
, const char *aTopic
,
303 const PRUnichar
*aData
) {
304 // We need to close any open notifications upon application exit, otherwise
305 // we will leak since libnotify holds a ref for us.
306 if (!nsCRT::strcmp(aTopic
, "quit-application") && mNotification
) {
307 g_signal_handler_disconnect(mNotification
, mClosureHandler
);
308 g_object_unref(mNotification
);
309 mNotification
= NULL
;
311 Release(); // equivalent to NS_RELEASE(this)
317 nsAlertsIconListener::InitAlertAsync(const nsAString
& aImageUrl
,
318 const nsAString
& aAlertTitle
,
319 const nsAString
& aAlertText
,
320 PRBool aAlertTextClickable
,
321 const nsAString
& aAlertCookie
,
322 nsIObserver
* aAlertListener
)
324 if (!notify_is_initted()) {
325 // Give the name of this application to libnotify
326 nsCOMPtr
<nsIStringBundleService
> bundleService
=
327 do_GetService(NS_STRINGBUNDLE_CONTRACTID
);
329 nsCAutoString appShortName
;
331 nsCOMPtr
<nsIStringBundle
> bundle
;
332 bundleService
->CreateBundle("chrome://branding/locale/brand.properties",
333 getter_AddRefs(bundle
));
334 nsAutoString appName
;
337 bundle
->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
338 getter_Copies(appName
));
339 appShortName
= NS_ConvertUTF16toUTF8(appName
);
341 NS_WARNING("brand.properties not present, using default application name");
342 appShortName
.AssignLiteral("Mozilla");
345 appShortName
.AssignLiteral("Mozilla");
348 if (!notify_init(appShortName
.get()))
349 return NS_ERROR_FAILURE
;
351 GList
*server_caps
= notify_get_server_caps();
353 for (GList
* cap
= server_caps
; cap
!= NULL
; cap
= cap
->next
) {
354 if (!strcmp((char*) cap
->data
, "actions")) {
355 gHasActions
= PR_TRUE
;
359 g_list_foreach(server_caps
, (GFunc
)g_free
, NULL
);
360 g_list_free(server_caps
);
364 if (!gHasActions
&& aAlertTextClickable
)
365 return NS_ERROR_FAILURE
; // No good, fallback to XUL
367 // Workaround for a libnotify bug - blank titles aren't dealt with
368 // properly so we use a space
369 if (aAlertTitle
.IsEmpty()) {
370 mAlertTitle
= NS_LITERAL_CSTRING(" ");
372 mAlertTitle
= NS_ConvertUTF16toUTF8(aAlertTitle
);
375 mAlertText
= NS_ConvertUTF16toUTF8(aAlertText
);
376 mAlertHasAction
= aAlertTextClickable
;
378 mAlertListener
= aAlertListener
;
379 mAlertCookie
= aAlertCookie
;
381 return StartRequest(aImageUrl
);