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

Side by Side Diff: lib/notification.js

Issue 29335650: Issue 2595 - Use the core code from adblockpluscore (Closed)
Patch Set: Created Feb. 4, 2016, 6:35 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 | « lib/matcher.js ('k') | lib/subscriptionClasses.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH
4 *
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
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
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/>.
16 */
17
18 /**
19 * @fileOverview Handles notifications.
20 */
21
22 Cu.import("resource://gre/modules/Services.jsm");
23
24 var {Prefs} = require("prefs");
25 var {Downloader, Downloadable, MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader");
26 var {Utils} = require("utils");
27 var {Matcher, defaultMatcher} = require("matcher");
28 var {Filter, RegExpFilter, WhitelistFilter} = require("filterClasses");
29
30 var INITIAL_DELAY = 1 * MILLIS_IN_MINUTE;
31 var CHECK_INTERVAL = 1 * MILLIS_IN_HOUR;
32 var EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY;
33 var TYPE = {
34 information: 0,
35 question: 1,
36 critical: 2
37 };
38
39 var showListeners = [];
40 var questionListeners = {};
41
42 function getNumericalSeverity(notification)
43 {
44 return (notification.type in TYPE ? TYPE[notification.type] : TYPE.information );
45 }
46
47 function saveNotificationData()
48 {
49 // HACK: JSON values aren't saved unless they are assigned a different object.
50 Prefs.notificationdata = JSON.parse(JSON.stringify(Prefs.notificationdata));
51 }
52
53 function localize(translations, locale)
54 {
55 if (locale in translations)
56 return translations[locale];
57
58 let languagePart = locale.substring(0, locale.indexOf("-"));
59 if (languagePart && languagePart in translations)
60 return translations[languagePart];
61
62 let defaultLocale = "en-US";
63 return translations[defaultLocale];
64 }
65
66 /**
67 * The object providing actual downloading functionality.
68 * @type Downloader
69 */
70 var downloader = null;
71 var localData = [];
72
73 /**
74 * Regularly fetches notifications and decides which to show.
75 * @class
76 */
77 var Notification = exports.Notification =
78 {
79 /**
80 * Called on module startup.
81 */
82 init: function()
83 {
84 downloader = new Downloader(this._getDownloadables.bind(this), INITIAL_DELAY , CHECK_INTERVAL);
85 downloader.onExpirationChange = this._onExpirationChange.bind(this);
86 downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this);
87 downloader.onDownloadError = this._onDownloadError.bind(this);
88 onShutdown.add(() => downloader.cancel());
89 },
90
91 /**
92 * Yields a Downloadable instances for the notifications download.
93 */
94 _getDownloadables: function*()
95 {
96 let downloadable = new Downloadable(Prefs.notificationurl);
97 if (typeof Prefs.notificationdata.lastError === "number")
98 downloadable.lastError = Prefs.notificationdata.lastError;
99 if (typeof Prefs.notificationdata.lastCheck === "number")
100 downloadable.lastCheck = Prefs.notificationdata.lastCheck;
101 if (typeof Prefs.notificationdata.data === "object" && "version" in Prefs.no tificationdata.data)
102 downloadable.lastVersion = Prefs.notificationdata.data.version;
103 if (typeof Prefs.notificationdata.softExpiration === "number")
104 downloadable.softExpiration = Prefs.notificationdata.softExpiration;
105 if (typeof Prefs.notificationdata.hardExpiration === "number")
106 downloadable.hardExpiration = Prefs.notificationdata.hardExpiration;
107 if (typeof Prefs.notificationdata.downloadCount === "number")
108 downloadable.downloadCount = Prefs.notificationdata.downloadCount;
109 yield downloadable;
110 },
111
112 _onExpirationChange: function(downloadable)
113 {
114 Prefs.notificationdata.lastCheck = downloadable.lastCheck;
115 Prefs.notificationdata.softExpiration = downloadable.softExpiration;
116 Prefs.notificationdata.hardExpiration = downloadable.hardExpiration;
117 saveNotificationData();
118 },
119
120 _onDownloadSuccess: function(downloadable, responseText, errorCallback, redire ctCallback)
121 {
122 try
123 {
124 let data = JSON.parse(responseText);
125 for (let notification of data.notifications)
126 {
127 if ("severity" in notification)
128 {
129 if (!("type" in notification))
130 notification.type = notification.severity;
131 delete notification.severity;
132 }
133 }
134 Prefs.notificationdata.data = data;
135 }
136 catch (e)
137 {
138 Cu.reportError(e);
139 errorCallback("synchronize_invalid_data");
140 return;
141 }
142
143 Prefs.notificationdata.lastError = 0;
144 Prefs.notificationdata.downloadStatus = "synchronize_ok";
145 [Prefs.notificationdata.softExpiration, Prefs.notificationdata.hardExpiratio n] = downloader.processExpirationInterval(EXPIRATION_INTERVAL);
146 Prefs.notificationdata.downloadCount = downloadable.downloadCount;
147 saveNotificationData();
148
149 Notification.showNext();
150 },
151
152 _onDownloadError: function(downloadable, downloadURL, error, channelStatus, re sponseStatus, redirectCallback)
153 {
154 Prefs.notificationdata.lastError = Date.now();
155 Prefs.notificationdata.downloadStatus = error;
156 saveNotificationData();
157 },
158
159 /**
160 * Adds a listener for notifications to be shown.
161 * @param {Function} listener Listener to be invoked when a notification is
162 * to be shown
163 */
164 addShowListener: function(listener)
165 {
166 if (showListeners.indexOf(listener) == -1)
167 showListeners.push(listener);
168 },
169
170 /**
171 * Removes the supplied listener.
172 * @param {Function} listener Listener that was added via addShowListener()
173 */
174 removeShowListener: function(listener)
175 {
176 let index = showListeners.indexOf(listener);
177 if (index != -1)
178 showListeners.splice(index, 1);
179 },
180
181 /**
182 * Determines which notification is to be shown next.
183 * @param {String} url URL to match notifications to (optional)
184 * @return {Object} notification to be shown, or null if there is none
185 */
186 _getNextToShow: function(url)
187 {
188 function checkTarget(target, parameter, name, version)
189 {
190 let minVersionKey = parameter + "MinVersion";
191 let maxVersionKey = parameter + "MaxVersion";
192 return !((parameter in target && target[parameter] != name) ||
193 (minVersionKey in target && Services.vc.compare(version, target[m inVersionKey]) < 0) ||
194 (maxVersionKey in target && Services.vc.compare(version, target[m axVersionKey]) > 0));
195 }
196
197 let remoteData = [];
198 if (typeof Prefs.notificationdata.data == "object" && Prefs.notificationdata .data.notifications instanceof Array)
199 remoteData = Prefs.notificationdata.data.notifications;
200
201 let notifications = localData.concat(remoteData);
202 if (notifications.length === 0)
203 return null;
204
205 let {addonName, addonVersion, application, applicationVersion, platform, pla tformVersion} = require("info");
206 let notificationToShow = null;
207 for (let notification of notifications)
208 {
209 if (typeof notification.type === "undefined" || notification.type !== "cri tical")
210 {
211 let shown = Prefs.notificationdata.shown;
212 if (shown instanceof Array && shown.indexOf(notification.id) != -1)
213 continue;
214 if (Prefs.notifications_ignoredcategories.indexOf("*") != -1)
215 continue;
216 }
217
218 if (typeof url === "string" || notification.urlFilters instanceof Array)
219 {
220 if (Prefs.enabled && typeof url === "string" && notification.urlFilters instanceof Array)
221 {
222 let host;
223 if (typeof URL == "function")
224 host = new URL(url).hostname;
225 else
226 {
227 try
228 {
229 host = Services.io.newURI(url, null, null).host;
230 }
231 catch (e)
232 {
233 // Ignore, an exception is expected for about: and similar schemes
234 host = "";
235 }
236 }
237 let exception = defaultMatcher.matchesAny(url, RegExpFilter.typeMap.DO CUMENT, host, false, null);
238 if (exception instanceof WhitelistFilter)
239 continue;
240
241 let matcher = new Matcher();
242 for (let urlFilter of notification.urlFilters)
243 matcher.add(Filter.fromText(urlFilter));
244 if (!matcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, host, fals e, null))
245 continue;
246 }
247 else
248 continue;
249 }
250
251 if (notification.targets instanceof Array)
252 {
253 let match = false;
254 for (let target of notification.targets)
255 {
256 if (checkTarget(target, "extension", addonName, addonVersion) &&
257 checkTarget(target, "application", application, applicationVersion ) &&
258 checkTarget(target, "platform", platform, platformVersion))
259 {
260 match = true;
261 break;
262 }
263 }
264 if (!match)
265 continue;
266 }
267
268 if (!notificationToShow
269 || getNumericalSeverity(notification) > getNumericalSeverity(notificat ionToShow))
270 notificationToShow = notification;
271 }
272
273 return notificationToShow;
274 },
275
276 /**
277 * Invokes the listeners added via addShowListener() with the next
278 * notification to be shown.
279 * @param {String} url URL to match notifications to (optional)
280 */
281 showNext: function(url)
282 {
283 let notification = Notification._getNextToShow(url);
284 if (notification)
285 for (let showListener of showListeners)
286 showListener(notification);
287 },
288
289 /**
290 * Marks a notification as shown.
291 * @param {String} id ID of the notification to be marked as shown
292 */
293 markAsShown: function(id)
294 {
295 var data = Prefs.notificationdata;
296
297 if (!(data.shown instanceof Array))
298 data.shown = [];
299 if (data.shown.indexOf(id) != -1)
300 return;
301
302 data.shown.push(id);
303 saveNotificationData();
304 },
305
306 /**
307 * Localizes the texts of the supplied notification.
308 * @param {Object} notification notification to translate
309 * @param {String} locale the target locale (optional, defaults to the
310 * application locale)
311 * @return {Object} the translated texts
312 */
313 getLocalizedTexts: function(notification, locale)
314 {
315 locale = locale || Utils.appLocale;
316 let textKeys = ["title", "message"];
317 let localizedTexts = [];
318 for (let key of textKeys)
319 {
320 if (key in notification)
321 {
322 if (typeof notification[key] == "string")
323 localizedTexts[key] = notification[key];
324 else
325 localizedTexts[key] = localize(notification[key], locale);
326 }
327 }
328 return localizedTexts;
329 },
330
331 /**
332 * Adds a local notification.
333 * @param {Object} notification notification to add
334 */
335 addNotification: function(notification)
336 {
337 if (localData.indexOf(notification) == -1)
338 localData.push(notification);
339 },
340
341 /**
342 * Removes an existing local notification.
343 * @param {Object} notification notification to remove
344 */
345 removeNotification: function(notification)
346 {
347 let index = localData.indexOf(notification);
348 if (index > -1)
349 localData.splice(index, 1);
350 },
351
352 /**
353 * Adds a listener for question-type notifications
354 */
355 addQuestionListener: function(/**string*/ id, /**function(approved)*/ listener )
356 {
357 if (!(id in questionListeners))
358 questionListeners[id] = [];
359 if (questionListeners[id].indexOf(listener) === -1)
360 questionListeners[id].push(listener);
361 },
362
363 /**
364 * Removes a listener that was previously added via addQuestionListener
365 */
366 removeQuestionListener: function(/**string*/ id, /**function(approved)*/ liste ner)
367 {
368 if (!(id in questionListeners))
369 return;
370 let index = questionListeners[id].indexOf(listener);
371 if (index > -1)
372 questionListeners[id].splice(index, 1);
373 if (questionListeners[id].length === 0)
374 delete questionListeners[id];
375 },
376
377 /**
378 * Notifies question listeners about interactions with a notification
379 * @param {String} id notification ID
380 * @param {Boolean} approved indicator whether notification has been approved or not
381 */
382 triggerQuestionListeners: function(id, approved)
383 {
384 if (!(id in questionListeners))
385 return;
386 let listeners = questionListeners[id];
387 for (let listener of listeners)
388 listener(approved);
389 },
390
391 /**
392 * Toggles whether notifications of a specific category should be ignored
393 * @param {String} category notification category identifier
394 * @param {Boolean} [forceValue] force specified value
395 */
396 toggleIgnoreCategory: function(category, forceValue)
397 {
398 let categories = Prefs.notifications_ignoredcategories;
399 let index = categories.indexOf(category);
400 if (index == -1 && forceValue !== false)
401 {
402 categories.push(category);
403 Prefs.notifications_showui = true;
404 }
405 else if (index != -1 && forceValue !== true)
406 categories.splice(index, 1);
407
408 // HACK: JSON values aren't saved unless they are assigned a different objec t.
409 Prefs.notifications_ignoredcategories = JSON.parse(JSON.stringify(categories ));
410 }
411 };
412 Notification.init();
OLDNEW
« no previous file with comments | « lib/matcher.js ('k') | lib/subscriptionClasses.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld