2012-03-16 15:23:28 -07:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2012-11-28 22:36:15 -08:00
|
|
|
|
2012-03-16 15:23:28 -07:00
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cu = Components.utils;
|
2012-11-28 22:36:15 -08:00
|
|
|
const Cc = Components.classes;
|
2012-03-16 15:23:28 -07:00
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
|
2012-12-03 06:42:36 -08:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
|
|
|
|
"@mozilla.org/system-message-internal;1",
|
|
|
|
"nsISystemMessagesInternal");
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "uuidGenerator",
|
|
|
|
"@mozilla.org/uuid-generator;1",
|
|
|
|
"nsIUUIDGenerator");
|
|
|
|
|
2014-03-18 09:01:13 -07:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "notificationStorage",
|
|
|
|
"@mozilla.org/notificationStorage;1",
|
|
|
|
"nsINotificationStorage");
|
|
|
|
|
2012-11-28 22:36:15 -08:00
|
|
|
XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
|
|
|
|
return Cc["@mozilla.org/childprocessmessagemanager;1"]
|
|
|
|
.getService(Ci.nsIMessageSender);
|
|
|
|
});
|
|
|
|
|
2012-12-03 06:42:36 -08:00
|
|
|
function debug(str) {
|
|
|
|
dump("=*= AlertsService.js : " + str + "\n");
|
|
|
|
}
|
|
|
|
|
2012-03-16 15:23:28 -07:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Alerts Service
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2014-05-01 02:23:00 -07:00
|
|
|
const kNotificationSystemMessageName = "notification";
|
|
|
|
|
|
|
|
const kMessageAppNotificationSend = "app-notification-send";
|
|
|
|
const kMessageAppNotificationReturn = "app-notification-return";
|
|
|
|
const kMessageAlertNotificationSend = "alert-notification-send";
|
|
|
|
const kMessageAlertNotificationClose = "alert-notification-close";
|
|
|
|
|
2014-08-06 00:45:00 -07:00
|
|
|
const kTopicAlertShow = "alertshow";
|
2014-05-01 02:23:00 -07:00
|
|
|
const kTopicAlertFinished = "alertfinished";
|
2014-05-08 14:16:00 -07:00
|
|
|
const kTopicAlertClickCallback = "alertclickcallback";
|
2014-05-01 02:23:00 -07:00
|
|
|
|
2012-11-28 22:36:15 -08:00
|
|
|
function AlertsService() {
|
2014-05-01 02:23:00 -07:00
|
|
|
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
|
|
|
cpmm.addMessageListener(kMessageAppNotificationReturn, this);
|
2012-11-28 22:36:15 -08:00
|
|
|
}
|
2012-03-16 15:23:28 -07:00
|
|
|
|
|
|
|
AlertsService.prototype = {
|
2012-07-26 15:25:02 -07:00
|
|
|
classID: Components.ID("{fe33c107-82a4-41d6-8c64-5353267e04c9}"),
|
2012-11-28 22:36:15 -08:00
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService,
|
2014-05-01 02:23:00 -07:00
|
|
|
Ci.nsIAppNotificationService,
|
|
|
|
Ci.nsIObserver]),
|
|
|
|
|
|
|
|
observe: function(aSubject, aTopic, aData) {
|
|
|
|
switch (aTopic) {
|
|
|
|
case "xpcom-shutdown":
|
|
|
|
Services.obs.removeObserver(this, "xpcom-shutdown");
|
|
|
|
cpmm.removeMessageListener(kMessageAppNotificationReturn, this);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
2012-03-16 15:23:28 -07:00
|
|
|
|
2012-11-28 22:36:15 -08:00
|
|
|
// nsIAlertsService
|
2014-05-01 02:23:00 -07:00
|
|
|
showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable,
|
|
|
|
aCookie, aAlertListener, aName, aBidi,
|
2014-11-28 11:08:29 -08:00
|
|
|
aLang, aDataStr, aPrincipal,
|
|
|
|
aInPrivateBrowsing) {
|
2014-05-01 02:23:00 -07:00
|
|
|
cpmm.sendAsyncMessage(kMessageAlertNotificationSend, {
|
|
|
|
imageURL: aImageUrl,
|
|
|
|
title: aTitle,
|
|
|
|
text: aText,
|
|
|
|
clickable: aTextClickable,
|
|
|
|
cookie: aCookie,
|
|
|
|
listener: aAlertListener,
|
|
|
|
id: aName,
|
|
|
|
dir: aBidi,
|
2014-08-20 17:56:12 -07:00
|
|
|
lang: aLang,
|
2014-11-28 11:08:29 -08:00
|
|
|
dataStr: aDataStr,
|
|
|
|
inPrivateBrowsing: aInPrivateBrowsing
|
2014-05-01 02:23:00 -07:00
|
|
|
});
|
2012-07-12 08:45:38 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
closeAlert: function(aName) {
|
2014-05-01 02:23:00 -07:00
|
|
|
cpmm.sendAsyncMessage(kMessageAlertNotificationClose, {
|
|
|
|
name: aName
|
|
|
|
});
|
2012-11-28 22:36:15 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// nsIAppNotificationService
|
2014-05-01 02:23:00 -07:00
|
|
|
showAppNotification: function(aImageURL, aTitle, aText, aAlertListener,
|
|
|
|
aDetails) {
|
2013-10-11 11:12:13 -07:00
|
|
|
let uid = (aDetails.id == "") ?
|
|
|
|
"app-notif-" + uuidGenerator.generateUUID() : aDetails.id;
|
2012-12-03 06:42:36 -08:00
|
|
|
|
2014-08-20 17:56:12 -07:00
|
|
|
let dataObj = this.deserializeStructuredClone(aDetails.data);
|
2012-12-03 06:42:36 -08:00
|
|
|
this._listeners[uid] = {
|
|
|
|
observer: aAlertListener,
|
|
|
|
title: aTitle,
|
|
|
|
text: aText,
|
2013-10-11 11:12:13 -07:00
|
|
|
manifestURL: aDetails.manifestURL,
|
2014-01-09 07:55:44 -08:00
|
|
|
imageURL: aImageURL,
|
|
|
|
lang: aDetails.lang || undefined,
|
|
|
|
id: aDetails.id || undefined,
|
2014-03-18 09:01:13 -07:00
|
|
|
dbId: aDetails.dbId || undefined,
|
2014-01-09 07:55:44 -08:00
|
|
|
dir: aDetails.dir || undefined,
|
2014-05-08 14:16:00 -07:00
|
|
|
tag: aDetails.tag || undefined,
|
2014-08-20 17:56:12 -07:00
|
|
|
timestamp: aDetails.timestamp || undefined,
|
|
|
|
dataObj: dataObj || undefined
|
2012-12-03 06:42:36 -08:00
|
|
|
};
|
|
|
|
|
2014-05-01 02:23:00 -07:00
|
|
|
cpmm.sendAsyncMessage(kMessageAppNotificationSend, {
|
2012-11-28 22:36:15 -08:00
|
|
|
imageURL: aImageURL,
|
|
|
|
title: aTitle,
|
|
|
|
text: aText,
|
2013-10-11 11:12:13 -07:00
|
|
|
uid: uid,
|
|
|
|
details: aDetails
|
2012-11-28 22:36:15 -08:00
|
|
|
});
|
2012-12-03 06:42:36 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// AlertsService.js custom implementation
|
|
|
|
_listeners: [],
|
|
|
|
|
2014-05-01 02:23:00 -07:00
|
|
|
receiveMessage: function(aMessage) {
|
2012-12-03 06:42:36 -08:00
|
|
|
let data = aMessage.data;
|
|
|
|
let listener = this._listeners[data.uid];
|
2014-05-01 02:23:00 -07:00
|
|
|
if (aMessage.name !== kMessageAppNotificationReturn || !listener) {
|
2012-12-03 06:42:36 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let topic = data.topic;
|
|
|
|
|
|
|
|
try {
|
|
|
|
listener.observer.observe(null, topic, null);
|
|
|
|
} catch (e) {
|
|
|
|
// It seems like there is no callbacks anymore, forward the click on
|
|
|
|
// notification via a system message containing the title/text/icon of
|
|
|
|
// the notification so the app get a change to react.
|
|
|
|
if (data.target) {
|
2014-08-06 00:45:00 -07:00
|
|
|
if (topic !== kTopicAlertShow) {
|
|
|
|
// excluding the 'show' event: there is no reason a unlaunched app
|
|
|
|
// would want to be notified that a notification is shown. This
|
|
|
|
// happens when a notification is still displayed at reboot time.
|
|
|
|
gSystemMessenger.sendMessage(kNotificationSystemMessageName, {
|
|
|
|
clicked: (topic === kTopicAlertClickCallback),
|
|
|
|
title: listener.title,
|
|
|
|
body: listener.text,
|
|
|
|
imageURL: listener.imageURL,
|
|
|
|
lang: listener.lang,
|
|
|
|
dir: listener.dir,
|
|
|
|
id: listener.id,
|
|
|
|
tag: listener.tag,
|
|
|
|
dbId: listener.dbId,
|
2014-08-20 17:56:12 -07:00
|
|
|
timestamp: listener.timestamp,
|
|
|
|
data: listener.dataObj || undefined,
|
2014-08-06 00:45:00 -07:00
|
|
|
},
|
|
|
|
Services.io.newURI(data.target, null, null),
|
|
|
|
Services.io.newURI(listener.manifestURL, null, null)
|
|
|
|
);
|
|
|
|
}
|
2012-12-03 06:42:36 -08:00
|
|
|
}
|
2014-09-12 07:50:00 -07:00
|
|
|
if (topic === kTopicAlertFinished && listener.dbId) {
|
|
|
|
notificationStorage.delete(listener.manifestURL, listener.dbId);
|
|
|
|
}
|
2012-12-03 06:42:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// we're done with this notification
|
2014-05-01 02:23:00 -07:00
|
|
|
if (topic === kTopicAlertFinished) {
|
2012-12-03 06:42:36 -08:00
|
|
|
delete this._listeners[data.uid];
|
|
|
|
}
|
2014-08-20 17:56:12 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
deserializeStructuredClone: function(dataString) {
|
|
|
|
if (!dataString) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
let scContainer = Cc["@mozilla.org/docshell/structured-clone-container;1"].
|
|
|
|
createInstance(Ci.nsIStructuredCloneContainer);
|
|
|
|
|
|
|
|
// The maximum supported structured-clone serialization format version
|
|
|
|
// as defined in "js/public/StructuredClone.h"
|
|
|
|
let JS_STRUCTURED_CLONE_VERSION = 4;
|
|
|
|
scContainer.initFromBase64(dataString, JS_STRUCTURED_CLONE_VERSION);
|
|
|
|
let dataObj = scContainer.deserializeToVariant();
|
|
|
|
|
|
|
|
// We have to check whether dataObj contains DOM objects (supported by
|
|
|
|
// nsIStructuredCloneContainer, but not by Cu.cloneInto), e.g. ImageData.
|
|
|
|
// After the structured clone callback systems will be unified, we'll not
|
|
|
|
// have to perform this check anymore.
|
|
|
|
try {
|
|
|
|
let data = Cu.cloneInto(dataObj, {});
|
|
|
|
} catch(e) { dataObj = null; }
|
|
|
|
|
|
|
|
return dataObj;
|
2012-03-16 15:23:28 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-10-31 09:13:28 -07:00
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AlertsService]);
|