|
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/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 const DEBUG = false; |
|
8 function debug(s) { dump("-*- NotificationStorage.js: " + s + "\n"); } |
|
9 |
|
10 const Cc = Components.classes; |
|
11 const Ci = Components.interfaces; |
|
12 const Cu = Components.utils; |
|
13 |
|
14 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
15 |
|
16 const NOTIFICATIONSTORAGE_CID = "{37f819b0-0b5c-11e3-8ffd-0800200c9a66}"; |
|
17 const NOTIFICATIONSTORAGE_CONTRACTID = "@mozilla.org/notificationStorage;1"; |
|
18 |
|
19 XPCOMUtils.defineLazyServiceGetter(this, "cpmm", |
|
20 "@mozilla.org/childprocessmessagemanager;1", |
|
21 "nsIMessageSender"); |
|
22 |
|
23 |
|
24 function NotificationStorage() { |
|
25 // cache objects |
|
26 this._notifications = {}; |
|
27 this._byTag = {}; |
|
28 this._cached = false; |
|
29 |
|
30 this._requests = {}; |
|
31 this._requestCount = 0; |
|
32 |
|
33 // Register for message listeners. |
|
34 cpmm.addMessageListener("Notification:GetAll:Return:OK", this); |
|
35 } |
|
36 |
|
37 NotificationStorage.prototype = { |
|
38 |
|
39 put: function(origin, id, title, dir, lang, body, tag, icon) { |
|
40 if (DEBUG) { debug("PUT: " + id + ": " + title); } |
|
41 var notification = { |
|
42 id: id, |
|
43 title: title, |
|
44 dir: dir, |
|
45 lang: lang, |
|
46 body: body, |
|
47 tag: tag, |
|
48 icon: icon, |
|
49 origin: origin |
|
50 }; |
|
51 |
|
52 this._notifications[id] = notification; |
|
53 if (tag) { |
|
54 if (!this._byTag[origin]) { |
|
55 this._byTag[origin] = {}; |
|
56 } |
|
57 |
|
58 // We might have existing notification with this tag, |
|
59 // if so we need to remove it from our cache. |
|
60 if (this._byTag[origin][tag]) { |
|
61 var oldNotification = this._byTag[origin][tag]; |
|
62 delete this._notifications[oldNotification.id]; |
|
63 } |
|
64 |
|
65 this._byTag[origin][tag] = notification; |
|
66 }; |
|
67 |
|
68 cpmm.sendAsyncMessage("Notification:Save", { |
|
69 origin: origin, |
|
70 notification: notification |
|
71 }); |
|
72 }, |
|
73 |
|
74 get: function(origin, tag, callback) { |
|
75 if (DEBUG) { debug("GET: " + origin + " " + tag); } |
|
76 if (this._cached) { |
|
77 this._fetchFromCache(origin, tag, callback); |
|
78 } else { |
|
79 this._fetchFromDB(origin, tag, callback); |
|
80 } |
|
81 }, |
|
82 |
|
83 delete: function(origin, id) { |
|
84 if (DEBUG) { debug("DELETE: " + id); } |
|
85 var notification = this._notifications[id]; |
|
86 if (notification) { |
|
87 if (notification.tag) { |
|
88 delete this._byTag[origin][notification.tag]; |
|
89 } |
|
90 delete this._notifications[id]; |
|
91 } |
|
92 |
|
93 cpmm.sendAsyncMessage("Notification:Delete", { |
|
94 origin: origin, |
|
95 id: id |
|
96 }); |
|
97 }, |
|
98 |
|
99 receiveMessage: function(message) { |
|
100 switch (message.name) { |
|
101 case "Notification:GetAll:Return:OK": |
|
102 var request = this._requests[message.data.requestID]; |
|
103 delete this._requests[message.data.requestID]; |
|
104 this._populateCache(message.data.notifications); |
|
105 this._fetchFromCache(request.origin, request.tag, request.callback); |
|
106 break; |
|
107 |
|
108 default: |
|
109 if (DEBUG) debug("Unrecognized message: " + message.name); |
|
110 break; |
|
111 } |
|
112 }, |
|
113 |
|
114 _fetchFromDB: function(origin, tag, callback) { |
|
115 var request = { |
|
116 origin: origin, |
|
117 tag: tag, |
|
118 callback: callback |
|
119 }; |
|
120 var requestID = this._requestCount++; |
|
121 this._requests[requestID] = request; |
|
122 cpmm.sendAsyncMessage("Notification:GetAll", { |
|
123 origin: origin, |
|
124 requestID: requestID |
|
125 }); |
|
126 }, |
|
127 |
|
128 _fetchFromCache: function(origin, tag, callback) { |
|
129 var notifications = []; |
|
130 // If a tag was specified and we have a notification |
|
131 // with this tag, return that. If no tag was specified |
|
132 // simple return all stored notifications. |
|
133 if (tag && this._byTag[origin] && this._byTag[origin][tag]) { |
|
134 notifications.push(this._byTag[origin][tag]); |
|
135 } else if (!tag) { |
|
136 for (var id in this._notifications) { |
|
137 if (this._notifications[id].origin === origin) { |
|
138 notifications.push(this._notifications[id]); |
|
139 } |
|
140 } |
|
141 } |
|
142 |
|
143 // Pass each notification back separately. |
|
144 notifications.forEach(function(notification) { |
|
145 try { |
|
146 callback.handle(notification.id, |
|
147 notification.title, |
|
148 notification.dir, |
|
149 notification.lang, |
|
150 notification.body, |
|
151 notification.tag, |
|
152 notification.icon); |
|
153 } catch (e) { |
|
154 if (DEBUG) { debug("Error calling callback handle: " + e); } |
|
155 } |
|
156 }); |
|
157 try { |
|
158 callback.done(); |
|
159 } catch (e) { |
|
160 if (DEBUG) { debug("Error calling callback done: " + e); } |
|
161 } |
|
162 }, |
|
163 |
|
164 _populateCache: function(notifications) { |
|
165 notifications.forEach(function(notification) { |
|
166 this._notifications[notification.id] = notification; |
|
167 if (notification.tag && notification.origin) { |
|
168 let tag = notification.tag; |
|
169 let origin = notification.origin; |
|
170 if (!this._byTag[origin]) { |
|
171 this._byTag[origin] = {}; |
|
172 } |
|
173 this._byTag[origin][tag] = notification; |
|
174 } |
|
175 }.bind(this)); |
|
176 this._cached = true; |
|
177 }, |
|
178 |
|
179 classID : Components.ID(NOTIFICATIONSTORAGE_CID), |
|
180 contractID : NOTIFICATIONSTORAGE_CONTRACTID, |
|
181 QueryInterface: XPCOMUtils.generateQI([Ci.nsINotificationStorage, |
|
182 Ci.nsIMessageListener]), |
|
183 }; |
|
184 |
|
185 |
|
186 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NotificationStorage]); |