diff options
Diffstat (limited to 'toolkit/components/webextensions/ext-notifications.js')
-rw-r--r-- | toolkit/components/webextensions/ext-notifications.js | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/toolkit/components/webextensions/ext-notifications.js b/toolkit/components/webextensions/ext-notifications.js new file mode 100644 index 000000000..1df96a2ac --- /dev/null +++ b/toolkit/components/webextensions/ext-notifications.js @@ -0,0 +1,161 @@ +"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"), + }, + }; +}); |