1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const Ci = Components.interfaces;
6 const Cu = Components.utils;
7 const Cc = Components.classes;
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
10 Cu.import("resource://gre/modules/Services.jsm");
12 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
13 "@mozilla.org/system-message-internal;1",
14 "nsISystemMessagesInternal");
16 XPCOMUtils.defineLazyServiceGetter(this, "uuidGenerator",
17 "@mozilla.org/uuid-generator;1",
20 XPCOMUtils.defineLazyServiceGetter(this, "notificationStorage",
21 "@mozilla.org/notificationStorage;1",
22 "nsINotificationStorage");
25 XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
26 return Cc["@mozilla.org/childprocessmessagemanager;1"]
27 .getService(Ci.nsIMessageSender);
31 dump("=*= AlertsService.js : " + str + "\n");
34 // -----------------------------------------------------------------------
36 // -----------------------------------------------------------------------
38 const kNotificationSystemMessageName = "notification";
40 const kMessageAppNotificationSend = "app-notification-send";
41 const kMessageAppNotificationReturn = "app-notification-return";
42 const kMessageAlertNotificationSend = "alert-notification-send";
43 const kMessageAlertNotificationClose = "alert-notification-close";
45 const kTopicAlertShow = "alertshow";
46 const kTopicAlertFinished = "alertfinished";
47 const kTopicAlertClickCallback = "alertclickcallback";
49 const kAllowOverwritePrefName =
50 "dom.notifications.mozchromenotifications.allow_resend_overwrite";
52 function AlertsService() {
53 this.trySetPrefValue();
54 Services.obs.addObserver(this, "xpcom-shutdown", false);
55 Services.prefs.addObserver(kAllowOverwritePrefName, this, false);
56 cpmm.addMessageListener(kMessageAppNotificationReturn, this);
59 AlertsService.prototype = {
60 classID: Components.ID("{fe33c107-82a4-41d6-8c64-5353267e04c9}"),
61 QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService,
62 Ci.nsIAppNotificationService,
65 trySetPrefValue: function() {
67 this.allowResendOverwrite =
68 Services.prefs.getBoolPref(kAllowOverwritePrefName);
70 this.allowResendOverwrite = false;
74 observe: function(aSubject, aTopic, aData) {
76 case "nsPref:changed":
77 if (aData === kAllowOverwritePrefName) {
78 this.trySetPrefValue();
82 case "xpcom-shutdown":
83 Services.obs.removeObserver(this, "xpcom-shutdown");
84 Services.prefs.removeObserver(kAllowOverwritePrefName, this);
85 cpmm.removeMessageListener(kMessageAppNotificationReturn, this);
91 showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable,
92 aCookie, aAlertListener, aName, aBidi,
94 cpmm.sendAsyncMessage(kMessageAlertNotificationSend, {
98 clickable: aTextClickable,
100 listener: aAlertListener,
108 closeAlert: function(aName) {
109 cpmm.sendAsyncMessage(kMessageAlertNotificationClose, {
114 // nsIAppNotificationService
115 showAppNotification: function(aImageURL, aTitle, aText, aAlertListener,
117 let uid = (aDetails.id == "") ?
118 "app-notif-" + uuidGenerator.generateUUID() : aDetails.id;
120 let dataObj = this.deserializeStructuredClone(aDetails.data);
122 if (this._listeners[uid]) {
123 // Is the notification we are dealing with is a resent one?
124 let currentIsResend = (aDetails.resent === true);
126 // Was the previous notification a resent one?
127 let oldIsResend = (this._listeners[uid].resent === true);
129 // Does the user allows resent notification to overwrite non resent ?
130 let denyOverwrite = this.allowResendOverwrite !== true;
132 // Are we in a case where we are trying to overwrite non resent with
134 let tentativeOverwrite = !oldIsResend && currentIsResend;
137 * If the current notification is a resent one and the previously
138 * recorded one was not a resent, let's check if we are allowed to
139 * overwrite. We do allow, however, to do multiple resends.
141 if (tentativeOverwrite && denyOverwrite) {
142 Cu.reportError("Notification " + uid + ": overwriting tentative by resend.");
147 this._listeners[uid] = {
148 observer: aAlertListener,
151 manifestURL: aDetails.manifestURL,
153 lang: aDetails.lang || undefined,
154 id: aDetails.id || undefined,
155 dbId: aDetails.dbId || undefined,
156 dir: aDetails.dir || undefined,
157 tag: aDetails.tag || undefined,
158 timestamp: aDetails.timestamp || undefined,
159 dataObj: dataObj || undefined,
160 resent: aDetails.resent || undefined
163 cpmm.sendAsyncMessage(kMessageAppNotificationSend, {
174 // AlertsService.js custom implementation
177 receiveMessage: function(aMessage) {
178 let data = aMessage.data;
179 let listener = this._listeners[data.uid];
180 if (aMessage.name !== kMessageAppNotificationReturn || !listener) {
184 let topic = data.topic;
187 listener.observer.observe(null, topic, null);
189 // It seems like there is no callbacks anymore, forward the click on
190 // notification via a system message containing the title/text/icon of
191 // the notification so the app get a change to react.
193 if (topic !== kTopicAlertShow) {
194 // excluding the 'show' event: there is no reason a unlaunched app
195 // would want to be notified that a notification is shown. This
196 // happens when a notification is still displayed at reboot time.
197 gSystemMessenger.sendMessage(kNotificationSystemMessageName, {
198 clicked: (topic === kTopicAlertClickCallback),
199 title: listener.title,
201 imageURL: listener.imageURL,
207 timestamp: listener.timestamp,
208 data: listener.dataObj || undefined,
210 Services.io.newURI(data.target, null, null),
211 Services.io.newURI(listener.manifestURL, null, null)
217 // we're done with this notification
218 if (topic === kTopicAlertFinished) {
220 notificationStorage.delete(listener.manifestURL, listener.dbId);
222 delete this._listeners[data.uid];
226 deserializeStructuredClone: function(dataString) {
230 let scContainer = Cc["@mozilla.org/docshell/structured-clone-container;1"].
231 createInstance(Ci.nsIStructuredCloneContainer);
233 // The maximum supported structured-clone serialization format version
234 // as defined in "js/public/StructuredClone.h"
235 let JS_STRUCTURED_CLONE_VERSION = 4;
236 scContainer.initFromBase64(dataString, JS_STRUCTURED_CLONE_VERSION);
237 let dataObj = scContainer.deserializeToVariant();
239 // We have to check whether dataObj contains DOM objects (supported by
240 // nsIStructuredCloneContainer, but not by Cu.cloneInto), e.g. ImageData.
241 // After the structured clone callback systems will be unified, we'll not
242 // have to perform this check anymore.
244 let data = Cu.cloneInto(dataObj, {});
245 } catch(e) { dataObj = null; }
251 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AlertsService]);