| OLD | NEW | 
|    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. |   41  */ | 
|   42  * @class |   42 class FilterStorage | 
|   43  */ |  | 
|   44 let FilterStorage = exports.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  Loading... | 
|  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  Loading... | 
|  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     let buf = []; |  489     let buf = []; | 
|  463  |  490  | 
|  464     // Save subscriptions |  491     // Save subscriptions | 
|  465     for (let subscription of subscriptions) |  492     for (let subscription of subscriptions) | 
|  466     { |  493     { | 
|  467       yield ""; |  494       yield ""; | 
|  468  |  495  | 
|  469       subscription.serialize(buf); |  496       subscription.serialize(buf); | 
| (...skipping 15 matching lines...) Expand all  Loading... | 
|  485         if (!saved.has(filter.text)) |  512         if (!saved.has(filter.text)) | 
|  486         { |  513         { | 
|  487           filter.serialize(buf); |  514           filter.serialize(buf); | 
|  488           saved.add(filter.text); |  515           saved.add(filter.text); | 
|  489           for (let line of buf) |  516           for (let line of buf) | 
|  490             yield line; |  517             yield line; | 
|  491           buf.splice(0); |  518           buf.splice(0); | 
|  492         } |  519         } | 
|  493       } |  520       } | 
|  494     } |  521     } | 
|  495   }, |  522   } | 
|  496  |  523  | 
|  497   /** |  524   /** | 
|  498    * Will be set to true if saveToDisk() is running (reentrance protection). |  525    * Saves all subscriptions back to disk. | 
|  499    * @type {boolean} |  526    * @returns {Promise} A promise resolved or rejected when saving is complete. | 
|  500    */ |  | 
|  501   _saving: false, |  | 
|  502  |  | 
|  503   /** |  | 
|  504    * Will be set to true if a saveToDisk() call arrives while saveToDisk() is |  | 
|  505    * already running (delayed execution). |  | 
|  506    * @type {boolean} |  | 
|  507    */ |  | 
|  508   _needsSave: false, |  | 
|  509  |  | 
|  510   /** |  | 
|  511    * Saves all subscriptions back to disk |  | 
|  512    * @return {Promise} promise resolved or rejected when saving is complete |  | 
|  513    */ |  527    */ | 
|  514   saveToDisk() |  528   saveToDisk() | 
|  515   { |  529   { | 
|  516     if (this._saving) |  530     if (this._saving) | 
|  517     { |  531     { | 
|  518       this._needsSave = true; |  532       this._needsSave = true; | 
|  519       return; |  533       return; | 
|  520     } |  534     } | 
|  521  |  535  | 
|  522     this._saving = true; |  536     this._saving = true; | 
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  586       Cu.reportError(error); |  600       Cu.reportError(error); | 
|  587     }).then(() => |  601     }).then(() => | 
|  588     { |  602     { | 
|  589       this._saving = false; |  603       this._saving = false; | 
|  590       if (this._needsSave) |  604       if (this._needsSave) | 
|  591       { |  605       { | 
|  592         this._needsSave = false; |  606         this._needsSave = false; | 
|  593         this.saveToDisk(); |  607         this.saveToDisk(); | 
|  594       } |  608       } | 
|  595     }); |  609     }); | 
|  596   }, |  610   } | 
|  597  |  611  | 
|  598   /** |  612   /** | 
|  599    * @typedef FileInfo |  613    * @typedef FileInfo | 
|  600    * @type {object} |  614    * @type {object} | 
|  601    * @property {number} index |  615    * @property {number} index | 
|  602    * @property {number} lastModified |  616    * @property {number} lastModified | 
|  603    */ |  617    */ | 
|  604  |  618  | 
|  605   /** |  619   /** | 
|  606    * Returns a promise resolving in a list of existing backup files. |  620    * Returns a promise resolving in a list of existing backup files. | 
|  607    * @return {Promise.<FileInfo[]>} |  621    * @returns {Promise.<Array.<FileInfo>>} | 
|  608    */ |  622    */ | 
|  609   getBackupFiles() |  623   getBackupFiles() | 
|  610   { |  624   { | 
|  611     let backups = []; |  625     let backups = []; | 
|  612  |  626  | 
|  613     let checkBackupFile = index => |  627     let checkBackupFile = index => | 
|  614     { |  628     { | 
|  615       return IO.statFile(this.getBackupName(index)).then(statData => |  629       return IO.statFile(this.getBackupName(index)).then(statData => | 
|  616       { |  630       { | 
|  617         if (!statData.exists) |  631         if (!statData.exists) | 
|  618           return backups; |  632           return backups; | 
|  619  |  633  | 
|  620         backups.push({ |  634         backups.push({ | 
|  621           index, |  635           index, | 
|  622           lastModified: statData.lastModified |  636           lastModified: statData.lastModified | 
|  623         }); |  637         }); | 
|  624         return checkBackupFile(index + 1); |  638         return checkBackupFile(index + 1); | 
|  625       }).catch(error => |  639       }).catch(error => | 
|  626       { |  640       { | 
|  627         // Something went wrong, return whatever data we got so far. |  641         // Something went wrong, return whatever data we got so far. | 
|  628         Cu.reportError(error); |  642         Cu.reportError(error); | 
|  629         return backups; |  643         return backups; | 
|  630       }); |  644       }); | 
|  631     }; |  645     }; | 
|  632  |  646  | 
|  633     return checkBackupFile(1); |  647     return checkBackupFile(1); | 
|  634   } |  648   } | 
|  635 }; |  649 } | 
|  636  |  650  | 
|  637 /** |  651 /** | 
|  638  * Joins subscription's filters to the subscription without any notifications. |  652  * Reads the user's filters from disk, manages them in memory, and writes them | 
|  639  * @param {Subscription} subscription |  653  * back to disk. | 
|  640  *   filter subscription that should be connected to its filters |  | 
|  641  */ |  654  */ | 
|  642 function addSubscriptionFilters(subscription) |  655 let filterStorage = new FilterStorage(); | 
 |  656  | 
 |  657 exports.filterStorage = filterStorage; | 
 |  658  | 
 |  659 /** | 
 |  660  * Connects a subscription to its filters without any notifications. | 
 |  661  * @param {Subscription} subscription The subscription that should be | 
 |  662  *   connected to its filters. | 
 |  663  */ | 
 |  664 function connectSubscriptionFilters(subscription) | 
|  643 { |  665 { | 
|  644   if (!FilterStorage.knownSubscriptions.has(subscription.url)) |  666   if (!filterStorage.knownSubscriptions.has(subscription.url)) | 
|  645     return; |  667     return; | 
|  646  |  668  | 
|  647   for (let filter of subscription.filters) |  669   for (let filter of subscription.filters) | 
|  648     filter.addSubscription(subscription); |  670     filter.addSubscription(subscription); | 
|  649 } |  671 } | 
|  650  |  672  | 
|  651 /** |  673 /** | 
|  652  * Removes subscription's filters from the subscription without any |  674  * Disconnects a subscription from its filters without any notifications. | 
|  653  * notifications. |  675  * @param {Subscription} subscription The subscription that should be | 
|  654  * @param {Subscription} subscription filter subscription to be removed |  676  *   disconnected from its filters. | 
|  655  */ |  677  */ | 
|  656 function removeSubscriptionFilters(subscription) |  678 function disconnectSubscriptionFilters(subscription) | 
|  657 { |  679 { | 
|  658   if (!FilterStorage.knownSubscriptions.has(subscription.url)) |  680   if (!filterStorage.knownSubscriptions.has(subscription.url)) | 
|  659     return; |  681     return; | 
|  660  |  682  | 
|  661   for (let filter of subscription.filters) |  683   for (let filter of subscription.filters) | 
|  662     filter.removeSubscription(subscription); |  684     filter.removeSubscription(subscription); | 
|  663 } |  685 } | 
| OLD | NEW |