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. 7, 2014, 5:08 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
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 TYPES = {
35 information: 0,
36 question: 1,
37 critical: 2
38 };
32 39
33 function getNumericalSeverity(notification) 40 function getNumericalSeverity(notification)
34 { 41 {
35 let levels = {information: 0, critical: 1}; 42 return (notification.type in TYPES ? TYPES[notification.type] : TYPES.informat ion);
Felix Dahlke 2014/02/11 10:35:27 I think we should be backwards compatible here, we
Thomas Greiner 2014/02/11 16:53:31 Done.
36 return (notification.severity in levels ? levels[notification.severity] : leve ls.information);
37 } 43 }
38 44
39 function saveNotificationData() 45 function saveNotificationData()
40 { 46 {
41 // HACK: JSON values aren't saved unless they are assigned a different object. 47 // HACK: JSON values aren't saved unless they are assigned a different object.
42 Prefs.notificationdata = JSON.parse(JSON.stringify(Prefs.notificationdata)); 48 Prefs.notificationdata = JSON.parse(JSON.stringify(Prefs.notificationdata));
43 } 49 }
44 50
45 function localize(translations, locale) 51 function localize(translations, locale)
46 { 52 {
47 if (locale in translations) 53 if (locale in translations)
48 return translations[locale]; 54 return translations[locale];
49 55
50 let languagePart = locale.substring(0, locale.indexOf("-")); 56 let languagePart = locale.substring(0, locale.indexOf("-"));
51 if (languagePart && languagePart in translations) 57 if (languagePart && languagePart in translations)
52 return translations[languagePart]; 58 return translations[languagePart];
53 59
54 let defaultLocale = "en-US"; 60 let defaultLocale = "en-US";
55 return translations[defaultLocale]; 61 return translations[defaultLocale];
56 } 62 }
57 63
58 /** 64 /**
59 * The object providing actual downloading functionality. 65 * The object providing actual downloading functionality.
60 * @type Downloader 66 * @type Downloader
61 */ 67 */
62 let downloader = null; 68 let downloader = null;
69 let localData = [];
63 70
64 /** 71 /**
65 * Regularly fetches notifications and decides which to show. 72 * Regularly fetches notifications and decides which to show.
66 * @class 73 * @class
67 */ 74 */
68 let Notification = exports.Notification = 75 let Notification = exports.Notification =
69 { 76 {
70 /** 77 /**
71 * Called on module startup. 78 * Called on module startup.
72 */ 79 */
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 142
136 _onDownloadError: function(downloadable, downloadURL, error, channelStatus, re sponseStatus, redirectCallback) 143 _onDownloadError: function(downloadable, downloadURL, error, channelStatus, re sponseStatus, redirectCallback)
137 { 144 {
138 Prefs.notificationdata.lastError = Date.now(); 145 Prefs.notificationdata.lastError = Date.now();
139 Prefs.notificationdata.downloadStatus = error; 146 Prefs.notificationdata.downloadStatus = error;
140 saveNotificationData(); 147 saveNotificationData();
141 }, 148 },
142 149
143 /** 150 /**
144 * Determines which notification is to be shown next. 151 * Determines which notification is to be shown next.
145 * @param {Array of Object} notifications active notifications 152 * @param {String} url URL to match notifications to
146 * @return {Object} notification to be shown, or null if there is none 153 * @return {Array of Object} active notifications
147 */ 154 */
148 getNextToShow: function() 155 _getActiveNotifications: function(url)
149 { 156 {
150 function checkTarget(target, parameter, name, version) 157 function checkTarget(target, parameter, name, version)
151 { 158 {
152 let minVersionKey = parameter + "MinVersion"; 159 let minVersionKey = parameter + "MinVersion";
153 let maxVersionKey = parameter + "MaxVersion"; 160 let maxVersionKey = parameter + "MaxVersion";
154 return !((parameter in target && target[parameter] != name) || 161 return !((parameter in target && target[parameter] != name) ||
155 (minVersionKey in target && Services.vc.compare(version, target[m inVersionKey]) < 0) || 162 (minVersionKey in target && Services.vc.compare(version, target[m inVersionKey]) < 0) ||
156 (maxVersionKey in target && Services.vc.compare(version, target[m axVersionKey]) > 0)); 163 (maxVersionKey in target && Services.vc.compare(version, target[m axVersionKey]) > 0));
157
158 } 164 }
159 165
160 if (typeof Prefs.notificationdata.data != "object" || !(Prefs.notificationda ta.data.notifications instanceof Array)) 166 let remoteData = [];
161 return null; 167 if (typeof Prefs.notificationdata.data == "object" && Prefs.notificationdata .data.notifications instanceof Array)
168 remoteData = Prefs.notificationdata.data.notifications;
162 169
163 if (!(Prefs.notificationdata.shown instanceof Array)) 170 if (!(Prefs.notificationdata.shown instanceof Array))
164 { 171 {
165 Prefs.notificationdata.shown = []; 172 Prefs.notificationdata.shown = [];
166 saveNotificationData(); 173 saveNotificationData();
167 } 174 }
168 175
169 let {addonName, addonVersion, application, applicationVersion, platform, pla tformVersion} = require("info"); 176 let {addonName, addonVersion, application, applicationVersion, platform, pla tformVersion} = require("info");
170 let notifications = Prefs.notificationdata.data.notifications; 177 let notifications = localData.concat(remoteData);
171 let notificationToShow = null; 178 let activeNotifications = [];
172 for each (let notification in notifications) 179 for each (let notification in notifications)
173 { 180 {
174 if ((typeof notification.severity === "undefined" || notification.severity === "information") 181 if ((typeof notification.type === "undefined" || notification.type === "in formation" || notification.type === "question")
Felix Dahlke 2014/02/11 10:35:27 I think this would make more sense now: if ((type
Thomas Greiner 2014/02/11 16:53:31 Done.
175 && Prefs.notificationdata.shown.indexOf(notification.id) !== -1) 182 && Prefs.notificationdata.shown.indexOf(notification.id) !== -1)
176 continue; 183 continue;
177 184
185 if (typeof url === "string" || notification.domains instanceof Array)
Felix Dahlke 2014/02/11 10:35:27 notification.domains are not necessarily domains i
Thomas Greiner 2014/02/11 16:53:31 Done.
186 {
187 if (typeof url === "string" && notification.domains instanceof Array)
188 {
189 let matcher = new Matcher();
Thomas Greiner 2014/02/07 17:29:17 Let me know if you don't like this approach. Howev
Felix Dahlke 2014/02/11 10:35:27 Pretty fine to me, wouldn't like to complicate thi
190 for each (let filter in notification.domains)
191 matcher.add(Filter.fromText(filter));
192 if (!matcher.matchesAny(url, "DOCUMENT", url))
193 continue;
194 }
195 else
196 continue;
Felix Dahlke 2014/02/11 10:35:27 So if the url parameter is passed, we ignore all n
Thomas Greiner 2014/02/11 16:53:31 This means that some notifications could be shown
Felix Dahlke 2014/02/11 17:11:12 Ah, I thought I'd change my mind on this after rev
197 }
198
178 if (notification.targets instanceof Array) 199 if (notification.targets instanceof Array)
179 { 200 {
180 let match = false; 201 let match = false;
181 for each (let target in notification.targets) 202 for each (let target in notification.targets)
182 { 203 {
183 if (checkTarget(target, "extension", addonName, addonVersion) && 204 if (checkTarget(target, "extension", addonName, addonVersion) &&
184 checkTarget(target, "application", application, applicationVersion ) && 205 checkTarget(target, "application", application, applicationVersion ) &&
185 checkTarget(target, "platform", platform, platformVersion)) 206 checkTarget(target, "platform", platform, platformVersion))
186 { 207 {
187 match = true; 208 match = true;
188 break; 209 break;
189 } 210 }
190 } 211 }
191 if (!match) 212 if (!match)
192 continue; 213 continue;
193 } 214 }
194 215
195 if (!notificationToShow 216 activeNotifications.push(notification);
Felix Dahlke 2014/02/11 10:35:27 Why did you change this logic? It looks to me like
Thomas Greiner 2014/02/11 16:53:31 Done.
196 || getNumericalSeverity(notification) > getNumericalSeverity(notificat ionToShow))
197 notificationToShow = notification;
198 } 217 }
199 218
219 return activeNotifications.sort(function(a, b)
220 {
221 return getNumericalSeverity(b) - getNumericalSeverity(a);
222 });
223 },
224
225 /**
226 * Determines which notification is to be shown next.
227 * @param {String} url URL to match notification to (optional)
228 * @return {Object} notification to be shown, or null if there is none
229 */
230 getNextToShow: function(url)
231 {
232 let [notificationToShow] = this._getActiveNotifications(url);
200 if (notificationToShow && "id" in notificationToShow) 233 if (notificationToShow && "id" in notificationToShow)
201 { 234 {
202 Prefs.notificationdata.shown.push(notificationToShow.id); 235 if (notificationToShow.type !== "question")
203 saveNotificationData(); 236 this.markAsShown(notificationToShow.id);
204 } 237 }
238 else
239 notificationToShow = null;
205 240
206 return notificationToShow; 241 return notificationToShow;
207 }, 242 },
208 243
244 markAsShown: function(id)
245 {
246 if (Prefs.notificationdata.shown.indexOf(id) > -1)
247 return;
248
249 Prefs.notificationdata.shown.push(id);
250 saveNotificationData();
251 },
252
209 /** 253 /**
210 * Localizes the texts of the supplied notification. 254 * Localizes the texts of the supplied notification.
211 * @param {Object} notification notification to translate 255 * @param {Object} notification notification to translate
212 * @param {String} locale the target locale (optional, defaults to the 256 * @param {String} locale the target locale (optional, defaults to the
213 * application locale) 257 * application locale)
214 * @return {Object} the translated texts 258 * @return {Object} the translated texts
215 */ 259 */
216 getLocalizedTexts: function(notification, locale) 260 getLocalizedTexts: function(notification, locale)
217 { 261 {
218 locale = locale || Utils.appLocale; 262 locale = locale || Utils.appLocale;
219 let textKeys = ["title", "message"]; 263 let textKeys = ["title", "message"];
220 let localizedTexts = []; 264 let localizedTexts = [];
221 for each (let key in textKeys) 265 for each (let key in textKeys)
222 { 266 {
223 if (key in notification) 267 if (key in notification)
224 localizedTexts[key] = localize(notification[key], locale); 268 {
269 if (typeof notification[key] == "string")
270 localizedTexts[key] = notification[key];
271 else
272 localizedTexts[key] = localize(notification[key], locale);
273 }
225 } 274 }
226 return localizedTexts; 275 return localizedTexts;
276 },
277
278 /**
279 * Adds a local notification.
280 * @param {Object} notification notification to add
281 */
282 addNotification: function(notification)
283 {
284 if (localData.indexOf(notification) == -1)
285 localData.push(notification);
286 },
287
288 /**
289 * Removes an existing local notification.
290 * @param {Object} notification notification to remove
291 */
292 removeNotification: function(notification)
293 {
294 if (localData.indexOf(notification) > -1)
Felix Dahlke 2014/02/11 10:35:27 Nit: Save the index to a temp instead of calling i
Thomas Greiner 2014/02/11 16:53:31 Done.
295 localData.splice(localData.indexOf(notification), 1);
227 } 296 }
228 }; 297 };
229 Notification.init(); 298 Notification.init();
OLDNEW

Powered by Google App Engine
This is Rietveld