michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const DEBUG = false; michael@0: function debug(s) { dump("-*- NotificationStorage.js: " + s + "\n"); } michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: const NOTIFICATIONSTORAGE_CID = "{37f819b0-0b5c-11e3-8ffd-0800200c9a66}"; michael@0: const NOTIFICATIONSTORAGE_CONTRACTID = "@mozilla.org/notificationStorage;1"; michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "cpmm", michael@0: "@mozilla.org/childprocessmessagemanager;1", michael@0: "nsIMessageSender"); michael@0: michael@0: michael@0: function NotificationStorage() { michael@0: // cache objects michael@0: this._notifications = {}; michael@0: this._byTag = {}; michael@0: this._cached = false; michael@0: michael@0: this._requests = {}; michael@0: this._requestCount = 0; michael@0: michael@0: // Register for message listeners. michael@0: cpmm.addMessageListener("Notification:GetAll:Return:OK", this); michael@0: } michael@0: michael@0: NotificationStorage.prototype = { michael@0: michael@0: put: function(origin, id, title, dir, lang, body, tag, icon) { michael@0: if (DEBUG) { debug("PUT: " + id + ": " + title); } michael@0: var notification = { michael@0: id: id, michael@0: title: title, michael@0: dir: dir, michael@0: lang: lang, michael@0: body: body, michael@0: tag: tag, michael@0: icon: icon, michael@0: origin: origin michael@0: }; michael@0: michael@0: this._notifications[id] = notification; michael@0: if (tag) { michael@0: if (!this._byTag[origin]) { michael@0: this._byTag[origin] = {}; michael@0: } michael@0: michael@0: // We might have existing notification with this tag, michael@0: // if so we need to remove it from our cache. michael@0: if (this._byTag[origin][tag]) { michael@0: var oldNotification = this._byTag[origin][tag]; michael@0: delete this._notifications[oldNotification.id]; michael@0: } michael@0: michael@0: this._byTag[origin][tag] = notification; michael@0: }; michael@0: michael@0: cpmm.sendAsyncMessage("Notification:Save", { michael@0: origin: origin, michael@0: notification: notification michael@0: }); michael@0: }, michael@0: michael@0: get: function(origin, tag, callback) { michael@0: if (DEBUG) { debug("GET: " + origin + " " + tag); } michael@0: if (this._cached) { michael@0: this._fetchFromCache(origin, tag, callback); michael@0: } else { michael@0: this._fetchFromDB(origin, tag, callback); michael@0: } michael@0: }, michael@0: michael@0: delete: function(origin, id) { michael@0: if (DEBUG) { debug("DELETE: " + id); } michael@0: var notification = this._notifications[id]; michael@0: if (notification) { michael@0: if (notification.tag) { michael@0: delete this._byTag[origin][notification.tag]; michael@0: } michael@0: delete this._notifications[id]; michael@0: } michael@0: michael@0: cpmm.sendAsyncMessage("Notification:Delete", { michael@0: origin: origin, michael@0: id: id michael@0: }); michael@0: }, michael@0: michael@0: receiveMessage: function(message) { michael@0: switch (message.name) { michael@0: case "Notification:GetAll:Return:OK": michael@0: var request = this._requests[message.data.requestID]; michael@0: delete this._requests[message.data.requestID]; michael@0: this._populateCache(message.data.notifications); michael@0: this._fetchFromCache(request.origin, request.tag, request.callback); michael@0: break; michael@0: michael@0: default: michael@0: if (DEBUG) debug("Unrecognized message: " + message.name); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: _fetchFromDB: function(origin, tag, callback) { michael@0: var request = { michael@0: origin: origin, michael@0: tag: tag, michael@0: callback: callback michael@0: }; michael@0: var requestID = this._requestCount++; michael@0: this._requests[requestID] = request; michael@0: cpmm.sendAsyncMessage("Notification:GetAll", { michael@0: origin: origin, michael@0: requestID: requestID michael@0: }); michael@0: }, michael@0: michael@0: _fetchFromCache: function(origin, tag, callback) { michael@0: var notifications = []; michael@0: // If a tag was specified and we have a notification michael@0: // with this tag, return that. If no tag was specified michael@0: // simple return all stored notifications. michael@0: if (tag && this._byTag[origin] && this._byTag[origin][tag]) { michael@0: notifications.push(this._byTag[origin][tag]); michael@0: } else if (!tag) { michael@0: for (var id in this._notifications) { michael@0: if (this._notifications[id].origin === origin) { michael@0: notifications.push(this._notifications[id]); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Pass each notification back separately. michael@0: notifications.forEach(function(notification) { michael@0: try { michael@0: callback.handle(notification.id, michael@0: notification.title, michael@0: notification.dir, michael@0: notification.lang, michael@0: notification.body, michael@0: notification.tag, michael@0: notification.icon); michael@0: } catch (e) { michael@0: if (DEBUG) { debug("Error calling callback handle: " + e); } michael@0: } michael@0: }); michael@0: try { michael@0: callback.done(); michael@0: } catch (e) { michael@0: if (DEBUG) { debug("Error calling callback done: " + e); } michael@0: } michael@0: }, michael@0: michael@0: _populateCache: function(notifications) { michael@0: notifications.forEach(function(notification) { michael@0: this._notifications[notification.id] = notification; michael@0: if (notification.tag && notification.origin) { michael@0: let tag = notification.tag; michael@0: let origin = notification.origin; michael@0: if (!this._byTag[origin]) { michael@0: this._byTag[origin] = {}; michael@0: } michael@0: this._byTag[origin][tag] = notification; michael@0: } michael@0: }.bind(this)); michael@0: this._cached = true; michael@0: }, michael@0: michael@0: classID : Components.ID(NOTIFICATIONSTORAGE_CID), michael@0: contractID : NOTIFICATIONSTORAGE_CONTRACTID, michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsINotificationStorage, michael@0: Ci.nsIMessageListener]), michael@0: }; michael@0: michael@0: michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NotificationStorage]);