Mypal/toolkit/components/webextensions/ext-notifications.js

162 lines
4.6 KiB
JavaScript

"use strict";
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://devtools/shared/event-emitter.js");
var {
EventManager,
ignoreEvent,
} = ExtensionUtils;
// WeakMap[Extension -> Map[id -> Notification]]
var notificationsMap = new WeakMap();
// Manages a notification popup (notifications API) created by the extension.
function Notification(extension, id, options) {
this.extension = extension;
this.id = id;
this.options = options;
let imageURL;
if (options.iconUrl) {
imageURL = this.extension.baseURI.resolve(options.iconUrl);
}
try {
let svc = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
svc.showAlertNotification(imageURL,
options.title,
options.message,
true, // textClickable
this.id,
this,
this.id);
} catch (e) {
// This will fail if alerts aren't available on the system.
}
}
Notification.prototype = {
clear() {
try {
let svc = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
svc.closeAlert(this.id);
} catch (e) {
// This will fail if the OS doesn't support this function.
}
notificationsMap.get(this.extension).delete(this.id);
},
observe(subject, topic, data) {
let notifications = notificationsMap.get(this.extension);
let emitAndDelete = event => {
notifications.emit(event, data);
notifications.delete(this.id);
};
// Don't try to emit events if the extension has been unloaded
if (!notifications) {
return;
}
if (topic === "alertclickcallback") {
emitAndDelete("clicked");
}
if (topic === "alertfinished") {
emitAndDelete("closed");
}
},
};
/* eslint-disable mozilla/balanced-listeners */
extensions.on("startup", (type, extension) => {
let map = new Map();
EventEmitter.decorate(map);
notificationsMap.set(extension, map);
});
extensions.on("shutdown", (type, extension) => {
if (notificationsMap.has(extension)) {
for (let notification of notificationsMap.get(extension).values()) {
notification.clear();
}
notificationsMap.delete(extension);
}
});
/* eslint-enable mozilla/balanced-listeners */
var nextId = 0;
extensions.registerSchemaAPI("notifications", "addon_parent", context => {
let {extension} = context;
return {
notifications: {
create: function(notificationId, options) {
if (!notificationId) {
notificationId = String(nextId++);
}
let notifications = notificationsMap.get(extension);
if (notifications.has(notificationId)) {
notifications.get(notificationId).clear();
}
// FIXME: Lots of options still aren't supported, especially
// buttons.
let notification = new Notification(extension, notificationId, options);
notificationsMap.get(extension).set(notificationId, notification);
return Promise.resolve(notificationId);
},
clear: function(notificationId) {
let notifications = notificationsMap.get(extension);
if (notifications.has(notificationId)) {
notifications.get(notificationId).clear();
return Promise.resolve(true);
}
return Promise.resolve(false);
},
getAll: function() {
let result = {};
notificationsMap.get(extension).forEach((value, key) => {
result[key] = value.options;
});
return Promise.resolve(result);
},
onClosed: new EventManager(context, "notifications.onClosed", fire => {
let listener = (event, notificationId) => {
// FIXME: Support the byUser argument.
fire(notificationId, true);
};
notificationsMap.get(extension).on("closed", listener);
return () => {
notificationsMap.get(extension).off("closed", listener);
};
}).api(),
onClicked: new EventManager(context, "notifications.onClicked", fire => {
let listener = (event, notificationId) => {
fire(notificationId, true);
};
notificationsMap.get(extension).on("clicked", listener);
return () => {
notificationsMap.get(extension).off("clicked", listener);
};
}).api(),
// Intend to implement this later: https://bugzilla.mozilla.org/show_bug.cgi?id=1190681
onButtonClicked: ignoreEvent(context, "notifications.onButtonClicked"),
},
};
});