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

Side by Side Diff: lib/notification.js

Issue 5330039625220096: Issue 1162 - Cache notification URL matcher
Patch Set: Fixed regression: Error if notification data not yet initialized Created Jan. 29, 2018, 3:36 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 | « no previous file | no next file » | 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 <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH 3 * Copyright (C) 2006-present 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
(...skipping 20 matching lines...) Expand all
31 31
32 const INITIAL_DELAY = 1 * MILLIS_IN_MINUTE; 32 const INITIAL_DELAY = 1 * MILLIS_IN_MINUTE;
33 const CHECK_INTERVAL = 1 * MILLIS_IN_HOUR; 33 const CHECK_INTERVAL = 1 * MILLIS_IN_HOUR;
34 const EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY; 34 const EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY;
35 const TYPE = { 35 const TYPE = {
36 information: 0, 36 information: 0,
37 question: 1, 37 question: 1,
38 relentless: 2, 38 relentless: 2,
39 critical: 3 39 critical: 3
40 }; 40 };
41 const MATCHER = Symbol("Notification matcher");
41 42
42 let showListeners = []; 43 let showListeners = [];
43 let questionListeners = {}; 44 let questionListeners = {};
44 45
45 function getNumericalSeverity(notification) 46 function getNumericalSeverity(notification)
46 { 47 {
47 if (notification.type in TYPE) 48 if (notification.type in TYPE)
48 return TYPE[notification.type]; 49 return TYPE[notification.type];
49 return TYPE.information; 50 return TYPE.information;
50 } 51 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
96 // Compare version suffix (e.g. 0.1alpha < 0.1b1 < 01.b2 < 0.1). 97 // Compare version suffix (e.g. 0.1alpha < 0.1b1 < 01.b2 < 0.1).
97 // However, note that this is a simple string comparision, meaning: b10 < b2 98 // However, note that this is a simple string comparision, meaning: b10 < b2
98 if (tail1 == tail2) 99 if (tail1 == tail2)
99 return 0; 100 return 0;
100 if (!tail1 || tail2 && tail1 > tail2) 101 if (!tail1 || tail2 && tail1 > tail2)
101 return 1; 102 return 1;
102 return -1; 103 return -1;
103 } 104 }
104 105
105 /** 106 /**
107 * Initializes notification's matcher based on notification's URL filters
108 * @param {Object} notification
109 */
110 function initNotificationMatcher(notification)
111 {
112 if (MATCHER in notification || !(notification.urlFilters instanceof Array))
113 return;
114
115 let matcher = new Matcher();
116 for (let urlFilter of notification.urlFilters)
117 {
118 matcher.add(Filter.fromText(urlFilter));
119 }
120 notification[MATCHER] = matcher;
121 }
122
123 /**
124 * Matches URL against notification's URL filters
125 * @param {Object} notification
126 * @param {string} [url]
127 * @return {boolean} whether notification and URL match
128 */
129 function matchesUrl(notification, url)
130 {
131 // No matching necessary if there's nothing to match
132 if (typeof url !== "string" && !(MATCHER in notification))
133 return true;
134
135 // Notification shouldn't match if extension is disabled
136 if (!Prefs.enabled)
137 return false;
138
139 // Notification shouldn't match if matching cannot be done
140 if (typeof url !== "string" || !(MATCHER in notification))
141 return false;
sergei 2018/01/30 17:25:05 It somehow conflicts with above if (typeof url !==
Thomas Greiner 2018/01/30 19:17:53 The logic behind it is that if the notification do
sergei 2018/01/30 21:06:48 Acknowledged.
142
143 let host;
144 try
145 {
146 host = new URL(url).hostname;
147 }
148 catch (e)
149 {
150 host = "";
151 }
152
153 // Notification shouldn't match if extension is disabled on provided domain
154 let exception = defaultMatcher.matchesAny(
155 url, RegExpFilter.typeMap.DOCUMENT, host, false, null
156 );
157 if (exception instanceof WhitelistFilter)
158 return false;
159
160 // Notification should match if one of its filters matches
161 let filter = notification[MATCHER].matchesAny(
162 url, RegExpFilter.typeMap.DOCUMENT, host, false, null
163 );
164 return !!filter;
165 }
166
167 /**
106 * The object providing actual downloading functionality. 168 * The object providing actual downloading functionality.
107 * @type {Downloader} 169 * @type {Downloader}
108 */ 170 */
109 let downloader = null; 171 let downloader = null;
172
173 /**
174 * List of notifications provided by the extension
175 * @type {Object[]}
176 */
110 let localData = []; 177 let localData = [];
111 178
112 /** 179 /**
113 * Regularly fetches notifications and decides which to show. 180 * Regularly fetches notifications and decides which to show.
114 * @class 181 * @class
115 */ 182 */
116 let Notification = exports.Notification = 183 let Notification = exports.Notification =
117 { 184 {
118 /** 185 /**
119 * Called on module startup. 186 * Called on module startup.
120 */ 187 */
121 init() 188 init()
122 { 189 {
190 let {data} = Prefs.notificationdata;
sergei 2018/01/30 17:25:05 Prefs' properties can be not ready yet, I would re
sergei 2018/01/30 17:28:24 Perhaps alternatively we could do it in `matchesUr
Thomas Greiner 2018/01/30 19:17:53 In theory that's correct but in practice this is n
sergei 2018/01/30 21:06:48 It does happen on practice (at least I observed th
kzar 2018/01/31 11:06:27 How about using the Prefs.untilLoaded Promise?
sergei 2018/02/05 12:49:34 It seems the best option.
191 if (data)
192 {
193 for (let notification of data.notifications)
194 {
195 initNotificationMatcher(notification);
196 }
197 }
198
123 downloader = new Downloader(this._getDownloadables.bind(this), 199 downloader = new Downloader(this._getDownloadables.bind(this),
124 INITIAL_DELAY, CHECK_INTERVAL); 200 INITIAL_DELAY, CHECK_INTERVAL);
125 downloader.onExpirationChange = this._onExpirationChange.bind(this); 201 downloader.onExpirationChange = this._onExpirationChange.bind(this);
126 downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this); 202 downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this);
127 downloader.onDownloadError = this._onDownloadError.bind(this); 203 downloader.onDownloadError = this._onDownloadError.bind(this);
128 onShutdown.add(() => downloader.cancel()); 204 onShutdown.add(() => downloader.cancel());
129 }, 205 },
130 206
131 /** 207 /**
132 * Yields a Downloadable instances for the notifications download. 208 * Yields a Downloadable instances for the notifications download.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 { 243 {
168 let data = JSON.parse(responseText); 244 let data = JSON.parse(responseText);
169 for (let notification of data.notifications) 245 for (let notification of data.notifications)
170 { 246 {
171 if ("severity" in notification) 247 if ("severity" in notification)
172 { 248 {
173 if (!("type" in notification)) 249 if (!("type" in notification))
174 notification.type = notification.severity; 250 notification.type = notification.severity;
175 delete notification.severity; 251 delete notification.severity;
176 } 252 }
253 initNotificationMatcher(notification);
177 } 254 }
178 Prefs.notificationdata.data = data; 255 Prefs.notificationdata.data = data;
179 } 256 }
180 catch (e) 257 catch (e)
181 { 258 {
182 Cu.reportError(e); 259 Cu.reportError(e);
183 errorCallback("synchronize_invalid_data"); 260 errorCallback("synchronize_invalid_data");
184 return; 261 return;
185 } 262 }
186 263
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 * @return {Object} notification to be shown, or null if there is none 309 * @return {Object} notification to be shown, or null if there is none
233 */ 310 */
234 _getNextToShow(url) 311 _getNextToShow(url)
235 { 312 {
236 let remoteData = []; 313 let remoteData = [];
237 if (typeof Prefs.notificationdata.data == "object" && 314 if (typeof Prefs.notificationdata.data == "object" &&
238 Prefs.notificationdata.data.notifications instanceof Array) 315 Prefs.notificationdata.data.notifications instanceof Array)
239 { 316 {
240 remoteData = Prefs.notificationdata.data.notifications; 317 remoteData = Prefs.notificationdata.data.notifications;
241 } 318 }
242
243 let notifications = localData.concat(remoteData); 319 let notifications = localData.concat(remoteData);
244 if (notifications.length === 0) 320 if (notifications.length === 0)
245 return null; 321 return null;
246 322
247 const {addonName, addonVersion, application, 323 const {addonName, addonVersion, application,
248 applicationVersion, platform, platformVersion} = require("info"); 324 applicationVersion, platform, platformVersion} = require("info");
249 325
250 let targetChecks = { 326 let targetChecks = {
251 extension: v => v == addonName, 327 extension: v => v == addonName,
252 extensionMinVersion: 328 extensionMinVersion:
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
291 continue; 367 continue;
292 } 368 }
293 369
294 if (notification.type !== "relentless" && 370 if (notification.type !== "relentless" &&
295 Prefs.notifications_ignoredcategories.indexOf("*") != -1) 371 Prefs.notifications_ignoredcategories.indexOf("*") != -1)
296 { 372 {
297 continue; 373 continue;
298 } 374 }
299 } 375 }
300 376
301 if (typeof url === "string" || notification.urlFilters instanceof Array) 377 if (!matchesUrl(notification, url))
302 { 378 continue;
303 if (Prefs.enabled && typeof url === "string" &&
304 notification.urlFilters instanceof Array)
305 {
306 let host;
307 try
308 {
309 host = new URL(url).hostname;
310 }
311 catch (e)
312 {
313 host = "";
314 }
315
316 let exception = defaultMatcher.matchesAny(
317 url, RegExpFilter.typeMap.DOCUMENT, host, false, null
318 );
319 if (exception instanceof WhitelistFilter)
320 continue;
321
322 let matcher = new Matcher();
323 for (let urlFilter of notification.urlFilters)
324 matcher.add(Filter.fromText(urlFilter));
325 if (!matcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, host,
326 false, null))
327 {
328 continue;
329 }
330 }
331 else
332 continue;
333 }
334 379
335 if (notification.targets instanceof Array) 380 if (notification.targets instanceof Array)
336 { 381 {
337 let match = false; 382 let match = false;
338 383
339 for (let target of notification.targets) 384 for (let target of notification.targets)
340 { 385 {
341 if (Object.keys(target).every(key => 386 if (Object.keys(target).every(key =>
342 targetChecks.hasOwnProperty(key) && 387 targetChecks.hasOwnProperty(key) &&
343 targetChecks[key](target[key]))) 388 targetChecks[key](target[key])))
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
426 return localizedTexts; 471 return localizedTexts;
427 }, 472 },
428 473
429 /** 474 /**
430 * Adds a local notification. 475 * Adds a local notification.
431 * @param {Object} notification notification to add 476 * @param {Object} notification notification to add
432 */ 477 */
433 addNotification(notification) 478 addNotification(notification)
434 { 479 {
435 if (localData.indexOf(notification) == -1) 480 if (localData.indexOf(notification) == -1)
481 {
482 initNotificationMatcher(notification);
436 localData.push(notification); 483 localData.push(notification);
484 }
437 }, 485 },
438 486
439 /** 487 /**
440 * Removes an existing local notification. 488 * Removes an existing local notification.
441 * @param {Object} notification notification to remove 489 * @param {Object} notification notification to remove
442 */ 490 */
443 removeNotification(notification) 491 removeNotification(notification)
444 { 492 {
445 let index = localData.indexOf(notification); 493 let index = localData.indexOf(notification);
446 if (index > -1) 494 if (index > -1)
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
514 else if (index != -1 && forceValue !== true) 562 else if (index != -1 && forceValue !== true)
515 categories.splice(index, 1); 563 categories.splice(index, 1);
516 564
517 // HACK: JSON values aren't saved unless they are assigned a 565 // HACK: JSON values aren't saved unless they are assigned a
518 // different object. 566 // different object.
519 Prefs.notifications_ignoredcategories = 567 Prefs.notifications_ignoredcategories =
520 JSON.parse(JSON.stringify(categories)); 568 JSON.parse(JSON.stringify(categories));
521 } 569 }
522 }; 570 };
523 Notification.init(); 571 Notification.init();
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld