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

Delta Between Two Patch Sets: lib/filterStorage.js

Issue 29900557: Issue 7016 - Convert serialization functions into generators (Closed)
Left Patch Set: Actually Address PS2 Comments Created Oct. 21, 2018, 5:25 p.m.
Right Patch Set: Actually address nits Created Oct. 23, 2018, 3:21 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « lib/filterClasses.js ('k') | lib/subscriptionClasses.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
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 "use strict"; 18 "use strict";
19 19
20 /** 20 /**
21 * @fileOverview FilterStorage class responsible for managing user's 21 * @fileOverview <code>filterStorage</code> object responsible for managing the
22 * subscriptions and filters. 22 * user's subscriptions and filters.
23 */ 23 */
24 24
25 const {IO} = require("io"); 25 const {IO} = require("io");
26 const {Prefs} = require("prefs"); 26 const {Prefs} = require("prefs");
27 const {Filter, ActiveFilter} = require("./filterClasses"); 27 const {Filter, ActiveFilter} = require("./filterClasses");
28 const {Subscription, SpecialSubscription, 28 const {Subscription, SpecialSubscription,
29 ExternalSubscription} = require("./subscriptionClasses"); 29 ExternalSubscription} = require("./subscriptionClasses");
30 const {filterNotifier} = require("./filterNotifier"); 30 const {filterNotifier} = require("./filterNotifier");
31 const {INIParser} = require("./iniParser"); 31 const {INIParser} = require("./iniParser");
32 32
33 /** 33 /**
34 * Version number of the filter storage file format. 34 * Version number of the filter storage file format.
35 * @type {number} 35 * @type {number}
36 */ 36 */
37 let formatVersion = 5; 37 const FORMAT_VERSION = 5;
38 38
39 /** 39 /**
40 * This class reads user's filters from disk, manages them in memory 40 * {@link filterStorage} implementation.
41 * and writes them back.
42 * @class
43 */ 41 */
44 let FilterStorage = exports.FilterStorage = 42 class FilterStorage
45 { 43 {
46 /** 44 /**
47 * Will be set to true after the initial loadFromDisk() call completes. 45 * @hideconstructor
48 * @type {boolean} 46 */
49 */ 47 constructor()
50 initialized: false, 48 {
51 49 /**
52 /** 50 * Will be set to true after the initial {@link FilterStorage#loadFromDisk}
53 * Version number of the patterns.ini format used. 51 * call completes.
52 * @type {boolean}
53 */
54 this.initialized = false;
55
56 /**
57 * Will be set to <code>true</code> if no <code>patterns.ini</code> file
58 * exists.
59 * @type {boolean}
60 */
61 this.firstRun = false;
62
63 /**
64 * Map of properties listed in the filter storage file before the sections
65 * start. Right now this should be only the format version.
66 * @type {object}
67 */
68 this.fileProperties = Object.create(null);
69
70 /**
71 * Map of subscriptions already on the list, by their URL/identifier.
72 * @type {Map.<string,Subscription>}
73 */
74 this.knownSubscriptions = new Map();
75
76 /**
77 * Will be set to true if {@link FilterStorage#saveToDisk} is running
78 * (reentrance protection).
79 * @type {boolean}
80 * @private
81 */
82 this._saving = false;
83
84 /**
85 * Will be set to true if a {@link FilterStorage#saveToDisk} call arrives
86 * while {@link FilterStorage#saveToDisk} is already running (delayed
87 * execution).
88 * @type {boolean}
89 * @private
90 */
91 this._needsSave = false;
92 }
93
94 /**
95 * The version number of the <code>patterns.ini</code> format used.
54 * @type {number} 96 * @type {number}
55 */ 97 */
56 get formatVersion() 98 get formatVersion()
57 { 99 {
58 return formatVersion; 100 return FORMAT_VERSION;
59 }, 101 }
60 102
61 /** 103 /**
62 * File containing the filter list 104 * The file containing the subscriptions.
63 * @type {string} 105 * @type {string}
64 */ 106 */
65 get sourceFile() 107 get sourceFile()
66 { 108 {
67 return "patterns.ini"; 109 return "patterns.ini";
68 }, 110 }
69 111
70 /** 112 /**
71 * Will be set to true if no patterns.ini file exists. 113 * Yields all subscriptions in the storage.
72 * @type {boolean}
73 */
74 firstRun: false,
75
76 /**
77 * Map of properties listed in the filter storage file before the sections
78 * start. Right now this should be only the format version.
79 */
80 fileProperties: Object.create(null),
81
82 /**
83 * Yields subscriptions containing all filters
84 * @yields {Subscription} 114 * @yields {Subscription}
85 */ 115 */
86 *subscriptions() 116 *subscriptions()
87 { 117 {
88 yield* this.knownSubscriptions.values(); 118 yield* this.knownSubscriptions.values();
89 }, 119 }
90 120
91 /** 121 /**
92 * Number of known subscriptions. 122 * The number of subscriptions in the storage.
93 * @type {number} 123 * @type {number}
94 */ 124 */
95 get subscriptionCount() 125 get subscriptionCount()
96 { 126 {
97 return this.knownSubscriptions.size; 127 return this.knownSubscriptions.size;
98 }, 128 }
99
100 /**
101 * Map of subscriptions already on the list, by their URL/identifier
102 * @type {Map.<string,Subscription>}
103 */
104 knownSubscriptions: new Map(),
105 129
106 /** 130 /**
107 * Finds the filter group that a filter should be added to by default. Will 131 * Finds the filter group that a filter should be added to by default. Will
108 * return null if this group doesn't exist yet. 132 * return <code>null</code> if this group doesn't exist yet.
109 * @param {Filter} filter 133 * @param {Filter} filter
110 * @return {?SpecialSubscription} 134 * @returns {?SpecialSubscription}
111 */ 135 */
112 getGroupForFilter(filter) 136 getGroupForFilter(filter)
113 { 137 {
114 let generalSubscription = null; 138 let generalSubscription = null;
115 for (let subscription of FilterStorage.knownSubscriptions.values()) 139 for (let subscription of this.knownSubscriptions.values())
116 { 140 {
117 if (subscription instanceof SpecialSubscription && !subscription.disabled) 141 if (subscription instanceof SpecialSubscription && !subscription.disabled)
118 { 142 {
119 // Always prefer specialized subscriptions 143 // Always prefer specialized subscriptions
120 if (subscription.isDefaultFor(filter)) 144 if (subscription.isDefaultFor(filter))
121 return subscription; 145 return subscription;
122 146
123 // If this is a general subscription - store it as fallback 147 // If this is a general subscription - store it as fallback
124 if (!generalSubscription && 148 if (!generalSubscription &&
125 (!subscription.defaults || !subscription.defaults.length)) 149 (!subscription.defaults || !subscription.defaults.length))
126 { 150 {
127 generalSubscription = subscription; 151 generalSubscription = subscription;
128 } 152 }
129 } 153 }
130 } 154 }
131 return generalSubscription; 155 return generalSubscription;
132 }, 156 }
133 157
134 /** 158 /**
135 * Adds a filter subscription to the list 159 * Adds a subscription to the storage.
136 * @param {Subscription} subscription filter subscription to be added 160 * @param {Subscription} subscription The subscription to be added.
137 */ 161 */
138 addSubscription(subscription) 162 addSubscription(subscription)
139 { 163 {
140 if (FilterStorage.knownSubscriptions.has(subscription.url)) 164 if (this.knownSubscriptions.has(subscription.url))
141 return; 165 return;
142 166
143 FilterStorage.knownSubscriptions.set(subscription.url, subscription); 167 this.knownSubscriptions.set(subscription.url, subscription);
144 addSubscriptionFilters(subscription); 168 connectSubscriptionFilters(subscription);
145 169
146 filterNotifier.emit("subscription.added", subscription); 170 filterNotifier.emit("subscription.added", subscription);
147 }, 171 }
148 172
149 /** 173 /**
150 * Removes a filter subscription from the list 174 * Removes a subscription from the storage.
151 * @param {Subscription} subscription filter subscription to be removed 175 * @param {Subscription} subscription The subscription to be removed.
152 */ 176 */
153 removeSubscription(subscription) 177 removeSubscription(subscription)
154 { 178 {
155 if (!FilterStorage.knownSubscriptions.has(subscription.url)) 179 if (!this.knownSubscriptions.has(subscription.url))
156 return; 180 return;
157 181
158 removeSubscriptionFilters(subscription); 182 disconnectSubscriptionFilters(subscription);
159 183
160 FilterStorage.knownSubscriptions.delete(subscription.url); 184 this.knownSubscriptions.delete(subscription.url);
161 185
162 // This should be the last remaining reference to the Subscription 186 // This should be the last remaining reference to the Subscription
163 // object. 187 // object.
164 Subscription.knownSubscriptions.delete(subscription.url); 188 Subscription.knownSubscriptions.delete(subscription.url);
165 189
166 filterNotifier.emit("subscription.removed", subscription); 190 filterNotifier.emit("subscription.removed", subscription);
167 }, 191 }
168 192
169 /** 193 /**
170 * Replaces the list of filters in a subscription by a new list 194 * Replaces the list of filters in a subscription with a new list.
171 * @param {Subscription} subscription filter subscription to be updated 195 * @param {Subscription} subscription The subscription to be updated.
172 * @param {Filter[]} filters new filter list 196 * @param {Array.<Filter>} filters The new list of filters.
173 */ 197 */
174 updateSubscriptionFilters(subscription, filters) 198 updateSubscriptionFilters(subscription, filters)
175 { 199 {
176 removeSubscriptionFilters(subscription); 200 disconnectSubscriptionFilters(subscription);
177 let oldFilters = subscription.filters; 201 let oldFilters = subscription.filters;
178 subscription.filters = filters; 202 subscription.filters = filters;
179 addSubscriptionFilters(subscription); 203 connectSubscriptionFilters(subscription);
180 filterNotifier.emit("subscription.updated", subscription, oldFilters); 204 filterNotifier.emit("subscription.updated", subscription, oldFilters);
181 }, 205 }
182 206
183 /** 207 /**
184 * Adds a user-defined filter to the list 208 * Adds a user-defined filter to the storage.
185 * @param {Filter} filter 209 * @param {Filter} filter
186 * @param {SpecialSubscription} [subscription] 210 * @param {?SpecialSubscription} [subscription] The subscription that the
187 * particular group that the filter should be added to 211 * filter should be added to.
188 * @param {number} [position] 212 * @param {number} [position] The position within the subscription at which
189 * position within the subscription at which the filter should be added 213 * the filter should be added. If not specified, the filter is added at the
214 * end of the subscription.
190 */ 215 */
191 addFilter(filter, subscription, position) 216 addFilter(filter, subscription, position)
192 { 217 {
193 if (!subscription) 218 if (!subscription)
194 { 219 {
195 for (let currentSubscription of filter.subscriptions()) 220 for (let currentSubscription of filter.subscriptions())
196 { 221 {
197 if (currentSubscription instanceof SpecialSubscription && 222 if (currentSubscription instanceof SpecialSubscription &&
198 !currentSubscription.disabled) 223 !currentSubscription.disabled)
199 { 224 {
200 return; // No need to add 225 return; // No need to add
201 } 226 }
202 } 227 }
203 subscription = FilterStorage.getGroupForFilter(filter); 228 subscription = this.getGroupForFilter(filter);
204 } 229 }
205 if (!subscription) 230 if (!subscription)
206 { 231 {
207 // No group for this filter exists, create one 232 // No group for this filter exists, create one
208 subscription = SpecialSubscription.createForFilter(filter); 233 subscription = SpecialSubscription.createForFilter(filter);
209 this.addSubscription(subscription); 234 this.addSubscription(subscription);
210 return; 235 return;
211 } 236 }
212 237
213 if (typeof position == "undefined") 238 if (typeof position == "undefined")
214 position = subscription.filters.length; 239 position = subscription.filters.length;
215 240
216 filter.addSubscription(subscription); 241 filter.addSubscription(subscription);
217 subscription.filters.splice(position, 0, filter); 242 subscription.filters.splice(position, 0, filter);
218 filterNotifier.emit("filter.added", filter, subscription, position); 243 filterNotifier.emit("filter.added", filter, subscription, position);
219 }, 244 }
220 245
221 /** 246 /**
222 * Removes a user-defined filter from the list 247 * Removes a user-defined filter from the storage.
223 * @param {Filter} filter 248 * @param {Filter} filter
224 * @param {SpecialSubscription} [subscription] a particular filter group that 249 * @param {?SpecialSubscription} [subscription] The subscription that the
225 * the filter should be removed from (if ommited will be removed from all 250 * filter should be removed from. If not specified, the filter will be
226 * subscriptions) 251 * removed from all subscriptions.
227 * @param {number} [position] position inside the filter group at which the 252 * @param {number} [position] The position within the subscription at which
228 * filter should be removed (if ommited all instances will be removed) 253 * the filter should be removed. If not specified, all instances of the
254 * filter will be removed.
229 */ 255 */
230 removeFilter(filter, subscription, position) 256 removeFilter(filter, subscription, position)
231 { 257 {
232 let subscriptions = ( 258 let subscriptions = (
233 subscription ? [subscription] : filter.subscriptions() 259 subscription ? [subscription] : filter.subscriptions()
234 ); 260 );
235 for (let currentSubscription of subscriptions) 261 for (let currentSubscription of subscriptions)
236 { 262 {
237 if (currentSubscription instanceof SpecialSubscription) 263 if (currentSubscription instanceof SpecialSubscription)
238 { 264 {
(...skipping 18 matching lines...) Expand all
257 { 283 {
258 currentSubscription.filters.splice(currentPosition, 1); 284 currentSubscription.filters.splice(currentPosition, 1);
259 if (currentSubscription.filters.indexOf(filter) < 0) 285 if (currentSubscription.filters.indexOf(filter) < 0)
260 filter.removeSubscription(currentSubscription); 286 filter.removeSubscription(currentSubscription);
261 filterNotifier.emit("filter.removed", filter, currentSubscription, 287 filterNotifier.emit("filter.removed", filter, currentSubscription,
262 currentPosition); 288 currentPosition);
263 } 289 }
264 } 290 }
265 } 291 }
266 } 292 }
267 }, 293 }
268 294
269 /** 295 /**
270 * Moves a user-defined filter to a new position 296 * Moves a user-defined filter to a new position.
271 * @param {Filter} filter 297 * @param {Filter} filter
272 * @param {SpecialSubscription} subscription filter group where the filter is 298 * @param {SpecialSubscription} subscription The subscription where the
273 * located 299 * filter is located.
274 * @param {number} oldPosition current position of the filter 300 * @param {number} oldPosition The current position of the filter.
275 * @param {number} newPosition new position of the filter 301 * @param {number} newPosition The new position of the filter.
276 */ 302 */
277 moveFilter(filter, subscription, oldPosition, newPosition) 303 moveFilter(filter, subscription, oldPosition, newPosition)
278 { 304 {
279 if (!(subscription instanceof SpecialSubscription) || 305 if (!(subscription instanceof SpecialSubscription) ||
280 subscription.filters[oldPosition] != filter) 306 subscription.filters[oldPosition] != filter)
281 { 307 {
282 return; 308 return;
283 } 309 }
284 310
285 newPosition = Math.min(Math.max(newPosition, 0), 311 newPosition = Math.min(Math.max(newPosition, 0),
286 subscription.filters.length - 1); 312 subscription.filters.length - 1);
287 if (oldPosition == newPosition) 313 if (oldPosition == newPosition)
288 return; 314 return;
289 315
290 subscription.filters.splice(oldPosition, 1); 316 subscription.filters.splice(oldPosition, 1);
291 subscription.filters.splice(newPosition, 0, filter); 317 subscription.filters.splice(newPosition, 0, filter);
292 filterNotifier.emit("filter.moved", filter, subscription, oldPosition, 318 filterNotifier.emit("filter.moved", filter, subscription, oldPosition,
293 newPosition); 319 newPosition);
294 }, 320 }
295 321
296 /** 322 /**
297 * Increases the hit count for a filter by one 323 * Increases the hit count for a filter by one.
298 * @param {Filter} filter 324 * @param {Filter} filter
299 */ 325 */
300 increaseHitCount(filter) 326 increaseHitCount(filter)
301 { 327 {
302 if (!Prefs.savestats || !(filter instanceof ActiveFilter)) 328 if (!Prefs.savestats || !(filter instanceof ActiveFilter))
303 return; 329 return;
304 330
305 filter.hitCount++; 331 filter.hitCount++;
306 filter.lastHit = Date.now(); 332 filter.lastHit = Date.now();
307 }, 333 }
308 334
309 /** 335 /**
310 * Resets hit count for some filters 336 * Resets hit count for some filters.
311 * @param {Filter[]} filters filters to be reset, if null all filters will 337 * @param {?Array.<Filter>} [filters] The filters to be reset. If not
312 * be reset 338 * specified, all filters will be reset.
313 */ 339 */
314 resetHitCounts(filters) 340 resetHitCounts(filters)
315 { 341 {
316 if (!filters) 342 if (!filters)
317 filters = Filter.knownFilters.values(); 343 filters = Filter.knownFilters.values();
318 for (let filter of filters) 344 for (let filter of filters)
319 { 345 {
320 filter.hitCount = 0; 346 filter.hitCount = 0;
321 filter.lastHit = 0; 347 filter.lastHit = 0;
322 } 348 }
323 }, 349 }
324 350
325 /** 351 /**
326 * @callback TextSink 352 * @callback TextSink
327 * @param {string?} line 353 * @param {string?} line
328 */ 354 */
329 355
330 /** 356 /**
331 * Allows importing previously serialized filter data. 357 * Allows importing previously serialized filter data.
332 * @param {boolean} silent 358 * @param {boolean} silent If <code>true</code>, no "load" notification will
333 * If true, no "load" notification will be sent out. 359 * be sent out.
334 * @return {TextSink} 360 * @returns {TextSink} The function to be called for each line of data.
335 * Function to be called for each line of data. Calling it with null as 361 * Calling it with <code>null</code> as the argument finalizes the import
336 * parameter finalizes the import and replaces existing data. No changes 362 * and replaces existing data. No changes will be applied before
337 * will be applied before finalization, so import can be "aborted" by 363 * finalization, so import can be "aborted" by forgetting this callback.
338 * forgetting this callback.
339 */ 364 */
340 importData(silent) 365 importData(silent)
341 { 366 {
342 let parser = new INIParser(); 367 let parser = new INIParser();
343 return line => 368 return line =>
344 { 369 {
345 parser.process(line); 370 parser.process(line);
346 if (line === null) 371 if (line === null)
347 { 372 {
348 let knownSubscriptions = new Map(); 373 let knownSubscriptions = new Map();
349 for (let subscription of parser.subscriptions) 374 for (let subscription of parser.subscriptions)
350 knownSubscriptions.set(subscription.url, subscription); 375 knownSubscriptions.set(subscription.url, subscription);
351 376
352 this.fileProperties = parser.fileProperties; 377 this.fileProperties = parser.fileProperties;
353 this.knownSubscriptions = knownSubscriptions; 378 this.knownSubscriptions = knownSubscriptions;
354 Filter.knownFilters = parser.knownFilters; 379 Filter.knownFilters = parser.knownFilters;
355 Subscription.knownSubscriptions = parser.knownSubscriptions; 380 Subscription.knownSubscriptions = parser.knownSubscriptions;
356 381
357 if (!silent) 382 if (!silent)
358 filterNotifier.emit("load"); 383 filterNotifier.emit("load");
359 } 384 }
360 }; 385 };
361 }, 386 }
362 387
363 /** 388 /**
364 * Loads all subscriptions from the disk. 389 * Loads all subscriptions from disk.
365 * @return {Promise} promise resolved or rejected when loading is complete 390 * @returns {Promise} A promise resolved or rejected when loading is complete.
366 */ 391 */
367 loadFromDisk() 392 loadFromDisk()
368 { 393 {
369 let tryBackup = backupIndex => 394 let tryBackup = backupIndex =>
370 { 395 {
371 return this.restoreBackup(backupIndex, true).then(() => 396 return this.restoreBackup(backupIndex, true).then(() =>
372 { 397 {
373 if (this.knownSubscriptions.size == 0) 398 if (this.knownSubscriptions.size == 0)
374 return tryBackup(backupIndex + 1); 399 return tryBackup(backupIndex + 1);
375 }).catch(error => 400 }).catch(error =>
(...skipping 22 matching lines...) Expand all
398 }); 423 });
399 }).catch(error => 424 }).catch(error =>
400 { 425 {
401 Cu.reportError(error); 426 Cu.reportError(error);
402 return tryBackup(1); 427 return tryBackup(1);
403 }).then(() => 428 }).then(() =>
404 { 429 {
405 this.initialized = true; 430 this.initialized = true;
406 filterNotifier.emit("load"); 431 filterNotifier.emit("load");
407 }); 432 });
408 }, 433 }
409 434
410 /** 435 /**
411 * Constructs the file name for a patterns.ini backup. 436 * Constructs the file name for a <code>patterns.ini</code> backup.
412 * @param {number} backupIndex 437 * @param {number} backupIndex Number of the backup file (1 being the most
413 * number of the backup file (1 being the most recent) 438 * recent).
414 * @return {string} backup file name 439 * @returns {string} Backup file name.
415 */ 440 */
416 getBackupName(backupIndex) 441 getBackupName(backupIndex)
417 { 442 {
418 let [name, extension] = this.sourceFile.split(".", 2); 443 let [name, extension] = this.sourceFile.split(".", 2);
419 return (name + "-backup" + backupIndex + "." + extension); 444 return (name + "-backup" + backupIndex + "." + extension);
420 }, 445 }
421 446
422 /** 447 /**
423 * Restores an automatically created backup. 448 * Restores an automatically created backup.
424 * @param {number} backupIndex 449 * @param {number} backupIndex Number of the backup to restore (1 being the
425 * number of the backup to restore (1 being the most recent) 450 * most recent).
426 * @param {boolean} silent 451 * @param {boolean} silent If <code>true</code>, no "load" notification will
427 * If true, no "load" notification will be sent out. 452 * be sent out.
428 * @return {Promise} promise resolved or rejected when restoring is complete 453 * @returns {Promise} A promise resolved or rejected when restoration is
454 * complete.
429 */ 455 */
430 restoreBackup(backupIndex, silent) 456 restoreBackup(backupIndex, silent)
431 { 457 {
432 let backupFile = this.getBackupName(backupIndex); 458 let backupFile = this.getBackupName(backupIndex);
433 let parser = this.importData(silent); 459 let parser = this.importData(silent);
434 return IO.readFromFile(backupFile, parser).then(() => 460 return IO.readFromFile(backupFile, parser).then(() =>
435 { 461 {
436 parser(null); 462 parser(null);
437 return this.saveToDisk(); 463 return this.saveToDisk();
438 }); 464 });
439 }, 465 }
440 466
441 /** 467 /**
442 * Generator serializing filter data and yielding it line by line. 468 * Generator serializing filter data and yielding it line by line.
469 * @yields {string}
443 */ 470 */
444 *exportData() 471 *exportData()
445 { 472 {
446 // Do not persist external subscriptions 473 // Do not persist external subscriptions
447 let subscriptions = []; 474 let subscriptions = [];
448 for (let subscription of this.subscriptions()) 475 for (let subscription of this.subscriptions())
449 { 476 {
450 if (!(subscription instanceof ExternalSubscription) && 477 if (!(subscription instanceof ExternalSubscription) &&
451 !(subscription instanceof SpecialSubscription && 478 !(subscription instanceof SpecialSubscription &&
452 subscription.filters.length == 0)) 479 subscription.filters.length == 0))
453 { 480 {
454 subscriptions.push(subscription); 481 subscriptions.push(subscription);
455 } 482 }
456 } 483 }
457 484
458 yield "# Adblock Plus preferences"; 485 yield "# Adblock Plus preferences";
459 yield "version=" + formatVersion; 486 yield "version=" + this.formatVersion;
460 487
461 let saved = new Set(); 488 let saved = new Set();
462 489
463 // Save subscriptions 490 // Save subscriptions
464 for (let subscription of subscriptions) 491 for (let subscription of subscriptions)
465 { 492 {
466 yield* subscription.serialize(); 493 yield* subscription.serialize();
467 494 yield* subscription.serializeFilters();
468 if (subscription.filters.length)
469 {
470 yield "[Subscription filters]";
Manish Jethani 2018/10/21 21:51:43 We could actually move this line into serializeFil
Jon Sonesen 2018/10/22 19:27:47 Yeah I will move it.
471 yield* subscription.serializeFilters();
472 }
473 } 495 }
474 496
475 // Save filter data 497 // Save filter data
476 for (let subscription of subscriptions) 498 for (let subscription of subscriptions)
477 { 499 {
478 for (let filter of subscription.filters) 500 for (let filter of subscription.filters)
479 { 501 {
480 if (!saved.has(filter.text)) 502 if (!saved.has(filter.text))
481 { 503 {
482 yield* filter.serialize(); 504 yield* filter.serialize();
483 saved.add(filter.text); 505 saved.add(filter.text);
484 } 506 }
485 } 507 }
486 } 508 }
487 }, 509 }
488 510
489 /** 511 /**
490 * Will be set to true if saveToDisk() is running (reentrance protection). 512 * Saves all subscriptions back to disk.
491 * @type {boolean} 513 * @returns {Promise} A promise resolved or rejected when saving is complete.
492 */
493 _saving: false,
494
495 /**
496 * Will be set to true if a saveToDisk() call arrives while saveToDisk() is
497 * already running (delayed execution).
498 * @type {boolean}
499 */
500 _needsSave: false,
501
502 /**
503 * Saves all subscriptions back to disk
504 * @return {Promise} promise resolved or rejected when saving is complete
505 */ 514 */
506 saveToDisk() 515 saveToDisk()
507 { 516 {
508 if (this._saving) 517 if (this._saving)
509 { 518 {
510 this._needsSave = true; 519 this._needsSave = true;
511 return; 520 return;
512 } 521 }
513 522
514 this._saving = true; 523 this._saving = true;
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
578 Cu.reportError(error); 587 Cu.reportError(error);
579 }).then(() => 588 }).then(() =>
580 { 589 {
581 this._saving = false; 590 this._saving = false;
582 if (this._needsSave) 591 if (this._needsSave)
583 { 592 {
584 this._needsSave = false; 593 this._needsSave = false;
585 this.saveToDisk(); 594 this.saveToDisk();
586 } 595 }
587 }); 596 });
588 }, 597 }
589 598
590 /** 599 /**
591 * @typedef FileInfo 600 * @typedef FileInfo
592 * @type {object} 601 * @type {object}
593 * @property {number} index 602 * @property {number} index
594 * @property {number} lastModified 603 * @property {number} lastModified
595 */ 604 */
596 605
597 /** 606 /**
598 * Returns a promise resolving in a list of existing backup files. 607 * Returns a promise resolving in a list of existing backup files.
599 * @return {Promise.<FileInfo[]>} 608 * @returns {Promise.<Array.<FileInfo>>}
600 */ 609 */
601 getBackupFiles() 610 getBackupFiles()
602 { 611 {
603 let backups = []; 612 let backups = [];
604 613
605 let checkBackupFile = index => 614 let checkBackupFile = index =>
606 { 615 {
607 return IO.statFile(this.getBackupName(index)).then(statData => 616 return IO.statFile(this.getBackupName(index)).then(statData =>
608 { 617 {
609 if (!statData.exists) 618 if (!statData.exists)
610 return backups; 619 return backups;
611 620
612 backups.push({ 621 backups.push({
613 index, 622 index,
614 lastModified: statData.lastModified 623 lastModified: statData.lastModified
615 }); 624 });
616 return checkBackupFile(index + 1); 625 return checkBackupFile(index + 1);
617 }).catch(error => 626 }).catch(error =>
618 { 627 {
619 // Something went wrong, return whatever data we got so far. 628 // Something went wrong, return whatever data we got so far.
620 Cu.reportError(error); 629 Cu.reportError(error);
621 return backups; 630 return backups;
622 }); 631 });
623 }; 632 };
624 633
625 return checkBackupFile(1); 634 return checkBackupFile(1);
626 } 635 }
627 }; 636 }
628 637
629 /** 638 /**
630 * Joins subscription's filters to the subscription without any notifications. 639 * Reads the user's filters from disk, manages them in memory, and writes them
631 * @param {Subscription} subscription 640 * back to disk.
632 * filter subscription that should be connected to its filters
633 */ 641 */
634 function addSubscriptionFilters(subscription) 642 let filterStorage = new FilterStorage();
643
644 exports.filterStorage = filterStorage;
645
646 /**
647 * Connects a subscription to its filters without any notifications.
648 * @param {Subscription} subscription The subscription that should be
649 * connected to its filters.
650 */
651 function connectSubscriptionFilters(subscription)
635 { 652 {
636 if (!FilterStorage.knownSubscriptions.has(subscription.url)) 653 if (!filterStorage.knownSubscriptions.has(subscription.url))
637 return; 654 return;
638 655
639 for (let filter of subscription.filters) 656 for (let filter of subscription.filters)
640 filter.addSubscription(subscription); 657 filter.addSubscription(subscription);
641 } 658 }
642 659
643 /** 660 /**
644 * Removes subscription's filters from the subscription without any 661 * Disconnects a subscription from its filters without any notifications.
645 * notifications. 662 * @param {Subscription} subscription The subscription that should be
646 * @param {Subscription} subscription filter subscription to be removed 663 * disconnected from its filters.
647 */ 664 */
648 function removeSubscriptionFilters(subscription) 665 function disconnectSubscriptionFilters(subscription)
649 { 666 {
650 if (!FilterStorage.knownSubscriptions.has(subscription.url)) 667 if (!filterStorage.knownSubscriptions.has(subscription.url))
651 return; 668 return;
652 669
653 for (let filter of subscription.filters) 670 for (let filter of subscription.filters)
654 filter.removeSubscription(subscription); 671 filter.removeSubscription(subscription);
655 } 672 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld