Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/notification.js

Issue 5538776168267776: Implemented anti-adblock message notification (Closed)
Patch Set: Created Feb. 13, 2014, 12:50 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « defaults/prefs.js ('k') | lib/ui.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2013 Eyeo GmbH 3 * Copyright (C) 2006-2013 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 /** 18 /**
19 * @fileOverview Handles notifications. 19 * @fileOverview Handles notifications.
20 */ 20 */
21 21
22 Cu.import("resource://gre/modules/Services.jsm"); 22 Cu.import("resource://gre/modules/Services.jsm");
23 23
24 let {TimeLine} = require("timeline"); 24 let {TimeLine} = require("timeline");
25 let {Prefs} = require("prefs"); 25 let {Prefs} = require("prefs");
26 let {Downloader, Downloadable, MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader"); 26 let {Downloader, Downloadable, MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader");
27 let {Utils} = require("utils"); 27 let {Utils} = require("utils");
28 let {Matcher} = require("matcher");
29 let {Filter} = require("filterClasses");
28 30
29 let INITIAL_DELAY = 12 * MILLIS_IN_MINUTE; 31 let INITIAL_DELAY = 12 * MILLIS_IN_MINUTE;
30 let CHECK_INTERVAL = 1 * MILLIS_IN_HOUR; 32 let CHECK_INTERVAL = 1 * MILLIS_IN_HOUR;
31 let EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY; 33 let EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY;
34 let TYPE = {
35 information: 0,
36 question: 1,
37 critical: 2
38 };
39
40 let listeners = {};
32 41
33 function getNumericalSeverity(notification) 42 function getNumericalSeverity(notification)
34 { 43 {
35 let levels = {information: 0, critical: 1}; 44 return (notification.type in TYPE ? TYPE[notification.type] : TYPE.information );
36 return (notification.severity in levels ? levels[notification.severity] : leve ls.information);
37 } 45 }
38 46
39 function saveNotificationData() 47 function saveNotificationData()
40 { 48 {
41 // HACK: JSON values aren't saved unless they are assigned a different object. 49 // HACK: JSON values aren't saved unless they are assigned a different object.
42 Prefs.notificationdata = JSON.parse(JSON.stringify(Prefs.notificationdata)); 50 Prefs.notificationdata = JSON.parse(JSON.stringify(Prefs.notificationdata));
43 } 51 }
44 52
45 function localize(translations, locale) 53 function localize(translations, locale)
46 { 54 {
47 if (locale in translations) 55 if (locale in translations)
48 return translations[locale]; 56 return translations[locale];
49 57
50 let languagePart = locale.substring(0, locale.indexOf("-")); 58 let languagePart = locale.substring(0, locale.indexOf("-"));
51 if (languagePart && languagePart in translations) 59 if (languagePart && languagePart in translations)
52 return translations[languagePart]; 60 return translations[languagePart];
53 61
54 let defaultLocale = "en-US"; 62 let defaultLocale = "en-US";
55 return translations[defaultLocale]; 63 return translations[defaultLocale];
56 } 64 }
57 65
58 /** 66 /**
59 * The object providing actual downloading functionality. 67 * The object providing actual downloading functionality.
60 * @type Downloader 68 * @type Downloader
61 */ 69 */
62 let downloader = null; 70 let downloader = null;
71 let localData = [];
63 72
64 /** 73 /**
65 * Regularly fetches notifications and decides which to show. 74 * Regularly fetches notifications and decides which to show.
66 * @class 75 * @class
67 */ 76 */
68 let Notification = exports.Notification = 77 let Notification = exports.Notification =
69 { 78 {
70 /** 79 /**
71 * Called on module startup. 80 * Called on module startup.
72 */ 81 */
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 Prefs.notificationdata.lastCheck = downloadable.lastCheck; 120 Prefs.notificationdata.lastCheck = downloadable.lastCheck;
112 Prefs.notificationdata.softExpiration = downloadable.softExpiration; 121 Prefs.notificationdata.softExpiration = downloadable.softExpiration;
113 Prefs.notificationdata.hardExpiration = downloadable.hardExpiration; 122 Prefs.notificationdata.hardExpiration = downloadable.hardExpiration;
114 saveNotificationData(); 123 saveNotificationData();
115 }, 124 },
116 125
117 _onDownloadSuccess: function(downloadable, responseText, errorCallback, redire ctCallback) 126 _onDownloadSuccess: function(downloadable, responseText, errorCallback, redire ctCallback)
118 { 127 {
119 try 128 try
120 { 129 {
121 Prefs.notificationdata.data = JSON.parse(responseText); 130 let data = JSON.parse(responseText);
131 for each (let notification in data.notifications)
132 {
133 if ("severity" in notification)
134 {
135 if (!("type" in notification))
136 notification.type = notification.severity;
137 delete notification.severity;
138 }
139 }
140 Prefs.notificationdata.data = data;
122 } 141 }
123 catch (e) 142 catch (e)
124 { 143 {
125 Cu.reportError(e); 144 Cu.reportError(e);
126 errorCallback("synchronize_invalid_data"); 145 errorCallback("synchronize_invalid_data");
127 return; 146 return;
128 } 147 }
129 148
130 Prefs.notificationdata.lastError = 0; 149 Prefs.notificationdata.lastError = 0;
131 Prefs.notificationdata.downloadStatus = "synchronize_ok"; 150 Prefs.notificationdata.downloadStatus = "synchronize_ok";
132 [Prefs.notificationdata.softExpiration, Prefs.notificationdata.hardExpiratio n] = downloader.processExpirationInterval(EXPIRATION_INTERVAL); 151 [Prefs.notificationdata.softExpiration, Prefs.notificationdata.hardExpiratio n] = downloader.processExpirationInterval(EXPIRATION_INTERVAL);
133 saveNotificationData(); 152 saveNotificationData();
134 }, 153 },
135 154
136 _onDownloadError: function(downloadable, downloadURL, error, channelStatus, re sponseStatus, redirectCallback) 155 _onDownloadError: function(downloadable, downloadURL, error, channelStatus, re sponseStatus, redirectCallback)
137 { 156 {
138 Prefs.notificationdata.lastError = Date.now(); 157 Prefs.notificationdata.lastError = Date.now();
139 Prefs.notificationdata.downloadStatus = error; 158 Prefs.notificationdata.downloadStatus = error;
140 saveNotificationData(); 159 saveNotificationData();
141 }, 160 },
142 161
143 /** 162 /**
144 * Determines which notification is to be shown next. 163 * Determines which notification is to be shown next.
145 * @param {Array of Object} notifications active notifications 164 * @param {String} url URL to match notifications to (optional)
146 * @return {Object} notification to be shown, or null if there is none 165 * @return {Object} notification to be shown, or null if there is none
147 */ 166 */
148 getNextToShow: function() 167 getNextToShow: function(url)
149 { 168 {
150 function checkTarget(target, parameter, name, version) 169 function checkTarget(target, parameter, name, version)
151 { 170 {
152 let minVersionKey = parameter + "MinVersion"; 171 let minVersionKey = parameter + "MinVersion";
153 let maxVersionKey = parameter + "MaxVersion"; 172 let maxVersionKey = parameter + "MaxVersion";
154 return !((parameter in target && target[parameter] != name) || 173 return !((parameter in target && target[parameter] != name) ||
155 (minVersionKey in target && Services.vc.compare(version, target[m inVersionKey]) < 0) || 174 (minVersionKey in target && Services.vc.compare(version, target[m inVersionKey]) < 0) ||
156 (maxVersionKey in target && Services.vc.compare(version, target[m axVersionKey]) > 0)); 175 (maxVersionKey in target && Services.vc.compare(version, target[m axVersionKey]) > 0));
157
158 } 176 }
159 177
160 if (typeof Prefs.notificationdata.data != "object" || !(Prefs.notificationda ta.data.notifications instanceof Array)) 178 let remoteData = [];
161 return null; 179 if (typeof Prefs.notificationdata.data == "object" && Prefs.notificationdata .data.notifications instanceof Array)
180 remoteData = Prefs.notificationdata.data.notifications;
162 181
163 if (!(Prefs.notificationdata.shown instanceof Array)) 182 if (!(Prefs.notificationdata.shown instanceof Array))
164 { 183 {
165 Prefs.notificationdata.shown = []; 184 Prefs.notificationdata.shown = [];
166 saveNotificationData(); 185 saveNotificationData();
167 } 186 }
168 187
188 let notifications = localData.concat(remoteData);
189 if (notifications.length === 0)
190 return null;
191
169 let {addonName, addonVersion, application, applicationVersion, platform, pla tformVersion} = require("info"); 192 let {addonName, addonVersion, application, applicationVersion, platform, pla tformVersion} = require("info");
170 let notifications = Prefs.notificationdata.data.notifications;
171 let notificationToShow = null; 193 let notificationToShow = null;
172 for each (let notification in notifications) 194 for each (let notification in notifications)
173 { 195 {
174 if ((typeof notification.severity === "undefined" || notification.severity === "information") 196 if ((typeof notification.type === "undefined" || notification.type !== "cr itical")
175 && Prefs.notificationdata.shown.indexOf(notification.id) !== -1) 197 && Prefs.notificationdata.shown.indexOf(notification.id) !== -1)
176 continue; 198 continue;
177 199
200 if (typeof url === "string" || notification.urlFilters instanceof Array)
201 {
202 if (typeof url === "string" && notification.urlFilters instanceof Array)
203 {
204 let matcher = new Matcher();
205 for each (let urlFilter in notification.urlFilters)
206 matcher.add(Filter.fromText(urlFilter));
207 if (!matcher.matchesAny(url, "DOCUMENT", url))
208 continue;
209 }
210 else
211 continue;
212 }
213
178 if (notification.targets instanceof Array) 214 if (notification.targets instanceof Array)
179 { 215 {
180 let match = false; 216 let match = false;
181 for each (let target in notification.targets) 217 for each (let target in notification.targets)
182 { 218 {
183 if (checkTarget(target, "extension", addonName, addonVersion) && 219 if (checkTarget(target, "extension", addonName, addonVersion) &&
184 checkTarget(target, "application", application, applicationVersion ) && 220 checkTarget(target, "application", application, applicationVersion ) &&
185 checkTarget(target, "platform", platform, platformVersion)) 221 checkTarget(target, "platform", platform, platformVersion))
186 { 222 {
187 match = true; 223 match = true;
188 break; 224 break;
189 } 225 }
190 } 226 }
191 if (!match) 227 if (!match)
192 continue; 228 continue;
193 } 229 }
194 230
195 if (!notificationToShow 231 if (!notificationToShow
196 || getNumericalSeverity(notification) > getNumericalSeverity(notificat ionToShow)) 232 || getNumericalSeverity(notification) > getNumericalSeverity(notificat ionToShow))
197 notificationToShow = notification; 233 notificationToShow = notification;
198 } 234 }
199 235
200 if (notificationToShow && "id" in notificationToShow) 236 if (notificationToShow && "id" in notificationToShow)
201 { 237 {
202 Prefs.notificationdata.shown.push(notificationToShow.id); 238 if (notificationToShow.type !== "question")
203 saveNotificationData(); 239 this.markAsShown(notificationToShow.id);
204 } 240 }
205 241
206 return notificationToShow; 242 return notificationToShow;
207 }, 243 },
208 244
245 markAsShown: function(id)
246 {
247 if (Prefs.notificationdata.shown.indexOf(id) > -1)
248 return;
249
250 Prefs.notificationdata.shown.push(id);
251 saveNotificationData();
252 },
253
209 /** 254 /**
210 * Localizes the texts of the supplied notification. 255 * Localizes the texts of the supplied notification.
211 * @param {Object} notification notification to translate 256 * @param {Object} notification notification to translate
212 * @param {String} locale the target locale (optional, defaults to the 257 * @param {String} locale the target locale (optional, defaults to the
213 * application locale) 258 * application locale)
214 * @return {Object} the translated texts 259 * @return {Object} the translated texts
215 */ 260 */
216 getLocalizedTexts: function(notification, locale) 261 getLocalizedTexts: function(notification, locale)
217 { 262 {
218 locale = locale || Utils.appLocale; 263 locale = locale || Utils.appLocale;
219 let textKeys = ["title", "message"]; 264 let textKeys = ["title", "message"];
220 let localizedTexts = []; 265 let localizedTexts = [];
221 for each (let key in textKeys) 266 for each (let key in textKeys)
222 { 267 {
223 if (key in notification) 268 if (key in notification)
224 localizedTexts[key] = localize(notification[key], locale); 269 {
270 if (typeof notification[key] == "string")
271 localizedTexts[key] = notification[key];
272 else
273 localizedTexts[key] = localize(notification[key], locale);
274 }
225 } 275 }
226 return localizedTexts; 276 return localizedTexts;
277 },
278
279 /**
280 * Adds a local notification.
281 * @param {Object} notification notification to add
282 */
283 addNotification: function(notification)
284 {
285 if (localData.indexOf(notification) == -1)
286 localData.push(notification);
287 },
288
289 /**
290 * Removes an existing local notification.
291 * @param {Object} notification notification to remove
292 */
293 removeNotification: function(notification)
294 {
295 let index = localData.indexOf(notification);
296 if (index > -1)
297 localData.splice(index, 1);
298 },
299
300 /**
301 * Adds a listener for question-type notifications
302 */
303 addQuestionListener: function(/**string*/ id, /**function(approved)*/ listener )
304 {
305 if (!(id in listeners))
306 listeners[id] = [];
307 if (listeners[id].indexOf(listener) === -1)
308 listeners[id].push(listener);
309 },
310
311 /**
312 * Removes a listener that was previously added via addQuestionListener
313 */
314 removeQuestionListener: function(/**string*/ id, /**function(approved)*/ liste ner)
315 {
316 if (!(id in listeners))
317 return;
318 let index = listeners[id].indexOf(listener);
319 if (index > -1)
320 listeners[id].splice(index, 1);
321 if (listeners[id].length === 0)
322 delete listeners[id];
323 },
324
325 /**
326 * Notifies listeners about interactions with a notification
327 * @param {String} id notification ID
328 * @param {Boolean} approved indicator whether notification has been approved or not
329 */
330 triggerQuestionListeners: function(id, approved)
331 {
332 if (!(id in listeners))
333 return;
334 for each (let listener in listeners[id])
335 listener(approved);
227 } 336 }
228 }; 337 };
229 Notification.init(); 338 Notification.init();
OLDNEW
« no previous file with comments | « defaults/prefs.js ('k') | lib/ui.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld