| LEFT | RIGHT | 
|    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 FilterStorage class responsible for managing user's | 
|   22  *               subscriptions and filters. |   22  *               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  |   32  | 
|   32 /** |   33 /** | 
|   33  * Version number of the filter storage file format. |   34  * Version number of the filter storage file format. | 
|   34  * @type {number} |   35  * @type {number} | 
|   35  */ |   36  */ | 
|   36 let formatVersion = 5; |   37 let formatVersion = 5; | 
|   37  |   38  | 
|   38 /** |   39 /** | 
|   39  * This class reads user's filters from disk, manages them in memory |   40  * This class reads user's filters from disk, manages them in memory | 
|   40  * and writes them back. |   41  * and writes them back. | 
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|   72    */ |   73    */ | 
|   73   firstRun: false, |   74   firstRun: false, | 
|   74  |   75  | 
|   75   /** |   76   /** | 
|   76    * Map of properties listed in the filter storage file before the sections |   77    * Map of properties listed in the filter storage file before the sections | 
|   77    * start. Right now this should be only the format version. |   78    * start. Right now this should be only the format version. | 
|   78    */ |   79    */ | 
|   79   fileProperties: Object.create(null), |   80   fileProperties: Object.create(null), | 
|   80  |   81  | 
|   81   /** |   82   /** | 
|   82    * List of filter subscriptions containing all filters |   83    * Yields subscriptions containing all filters | 
|   83    * @type {Subscription[]} |   84    * @yields {Subscription} | 
|   84    */ |   85    */ | 
|   85   subscriptions: [], |   86   *subscriptions() | 
 |   87   { | 
 |   88     yield* this.knownSubscriptions.values(); | 
 |   89   }, | 
 |   90  | 
 |   91   /** | 
 |   92    * Number of known subscriptions. | 
 |   93    * @type {number} | 
 |   94    */ | 
 |   95   get subscriptionCount() | 
 |   96   { | 
 |   97     return this.knownSubscriptions.size; | 
 |   98   }, | 
|   86  |   99  | 
|   87   /** |  100   /** | 
|   88    * Map of subscriptions already on the list, by their URL/identifier |  101    * Map of subscriptions already on the list, by their URL/identifier | 
|   89    * @type {Map.<string,Subscription>} |  102    * @type {Map.<string,Subscription>} | 
|   90    */ |  103    */ | 
|   91   knownSubscriptions: new Map(), |  104   knownSubscriptions: new Map(), | 
|   92  |  105  | 
|   93   /** |  106   /** | 
|   94    * Finds the filter group that a filter should be added to by default. Will |  107    * Finds the filter group that a filter should be added to by default. Will | 
|   95    * return null if this group doesn't exist yet. |  108    * return null if this group doesn't exist yet. | 
|   96    * @param {Filter} filter |  109    * @param {Filter} filter | 
|   97    * @return {?SpecialSubscription} |  110    * @return {?SpecialSubscription} | 
|   98    */ |  111    */ | 
|   99   getGroupForFilter(filter) |  112   getGroupForFilter(filter) | 
|  100   { |  113   { | 
|  101     let generalSubscription = null; |  114     let generalSubscription = null; | 
|  102     for (let subscription of FilterStorage.subscriptions) |  115     for (let subscription of FilterStorage.knownSubscriptions.values()) | 
|  103     { |  116     { | 
|  104       if (subscription instanceof SpecialSubscription && !subscription.disabled) |  117       if (subscription instanceof SpecialSubscription && !subscription.disabled) | 
|  105       { |  118       { | 
|  106         // Always prefer specialized subscriptions |  119         // Always prefer specialized subscriptions | 
|  107         if (subscription.isDefaultFor(filter)) |  120         if (subscription.isDefaultFor(filter)) | 
|  108           return subscription; |  121           return subscription; | 
|  109  |  122  | 
|  110         // If this is a general subscription - store it as fallback |  123         // If this is a general subscription - store it as fallback | 
|  111         if (!generalSubscription && |  124         if (!generalSubscription && | 
|  112             (!subscription.defaults || !subscription.defaults.length)) |  125             (!subscription.defaults || !subscription.defaults.length)) | 
|  113         { |  126         { | 
|  114           generalSubscription = subscription; |  127           generalSubscription = subscription; | 
|  115         } |  128         } | 
|  116       } |  129       } | 
|  117     } |  130     } | 
|  118     return generalSubscription; |  131     return generalSubscription; | 
|  119   }, |  132   }, | 
|  120  |  133  | 
|  121   /** |  134   /** | 
|  122    * Adds a filter subscription to the list |  135    * Adds a filter subscription to the list | 
|  123    * @param {Subscription} subscription filter subscription to be added |  136    * @param {Subscription} subscription filter subscription to be added | 
|  124    */ |  137    */ | 
|  125   addSubscription(subscription) |  138   addSubscription(subscription) | 
|  126   { |  139   { | 
|  127     if (FilterStorage.knownSubscriptions.has(subscription.url)) |  140     if (FilterStorage.knownSubscriptions.has(subscription.url)) | 
|  128       return; |  141       return; | 
|  129  |  142  | 
|  130     FilterStorage.subscriptions.push(subscription); |  | 
|  131     FilterStorage.knownSubscriptions.set(subscription.url, subscription); |  143     FilterStorage.knownSubscriptions.set(subscription.url, subscription); | 
|  132     addSubscriptionFilters(subscription); |  144     addSubscriptionFilters(subscription); | 
|  133  |  145  | 
|  134     FilterNotifier.triggerListeners("subscription.added", subscription); |  146     filterNotifier.emit("subscription.added", subscription); | 
|  135   }, |  147   }, | 
|  136  |  148  | 
|  137   /** |  149   /** | 
|  138    * Removes a filter subscription from the list |  150    * Removes a filter subscription from the list | 
|  139    * @param {Subscription} subscription filter subscription to be removed |  151    * @param {Subscription} subscription filter subscription to be removed | 
|  140    */ |  152    */ | 
|  141   removeSubscription(subscription) |  153   removeSubscription(subscription) | 
|  142   { |  154   { | 
|  143     for (let i = 0; i < FilterStorage.subscriptions.length; i++) |  155     if (!FilterStorage.knownSubscriptions.has(subscription.url)) | 
|  144     { |  | 
|  145       if (FilterStorage.subscriptions[i].url == subscription.url) |  | 
|  146       { |  | 
|  147         removeSubscriptionFilters(subscription); |  | 
|  148  |  | 
|  149         FilterStorage.subscriptions.splice(i--, 1); |  | 
|  150         FilterStorage.knownSubscriptions.delete(subscription.url); |  | 
|  151         FilterNotifier.triggerListeners("subscription.removed", subscription); |  | 
|  152         return; |  | 
|  153       } |  | 
|  154     } |  | 
|  155   }, |  | 
|  156  |  | 
|  157   /** |  | 
|  158    * Moves a subscription in the list to a new position. |  | 
|  159    * @param {Subscription} subscription filter subscription to be moved |  | 
|  160    * @param {Subscription} [insertBefore] filter subscription to insert before |  | 
|  161    *        (if omitted the subscription will be put at the end of the list) |  | 
|  162    */ |  | 
|  163   moveSubscription(subscription, insertBefore) |  | 
|  164   { |  | 
|  165     let currentPos = FilterStorage.subscriptions.indexOf(subscription); |  | 
|  166     if (currentPos < 0) |  | 
|  167       return; |  156       return; | 
|  168  |  157  | 
|  169     let newPos = -1; |  158     removeSubscriptionFilters(subscription); | 
|  170     if (insertBefore) |  159  | 
|  171       newPos = FilterStorage.subscriptions.indexOf(insertBefore); |  160     FilterStorage.knownSubscriptions.delete(subscription.url); | 
|  172  |  161  | 
|  173     if (newPos < 0) |  162     // This should be the last remaining reference to the Subscription | 
|  174       newPos = FilterStorage.subscriptions.length; |  163     // object. | 
|  175  |  164     Subscription.knownSubscriptions.delete(subscription.url); | 
|  176     if (currentPos < newPos) |  165  | 
|  177       newPos--; |  166     filterNotifier.emit("subscription.removed", subscription); | 
|  178     if (currentPos == newPos) |  | 
|  179       return; |  | 
|  180  |  | 
|  181     FilterStorage.subscriptions.splice(currentPos, 1); |  | 
|  182     FilterStorage.subscriptions.splice(newPos, 0, subscription); |  | 
|  183     FilterNotifier.triggerListeners("subscription.moved", subscription); |  | 
|  184   }, |  167   }, | 
|  185  |  168  | 
|  186   /** |  169   /** | 
|  187    * Replaces the list of filters in a subscription by a new list |  170    * Replaces the list of filters in a subscription by a new list | 
|  188    * @param {Subscription} subscription filter subscription to be updated |  171    * @param {Subscription} subscription filter subscription to be updated | 
|  189    * @param {Filter[]} filters new filter list |  172    * @param {Filter[]} filters new filter list | 
|  190    */ |  173    */ | 
|  191   updateSubscriptionFilters(subscription, filters) |  174   updateSubscriptionFilters(subscription, filters) | 
|  192   { |  175   { | 
|  193     removeSubscriptionFilters(subscription); |  176     removeSubscriptionFilters(subscription); | 
|  194     subscription.oldFilters = subscription.filters; |  177     let oldFilters = subscription.filters; | 
|  195     subscription.filters = filters; |  178     subscription.filters = filters; | 
|  196     addSubscriptionFilters(subscription); |  179     addSubscriptionFilters(subscription); | 
|  197     FilterNotifier.triggerListeners("subscription.updated", subscription); |  180     filterNotifier.emit("subscription.updated", subscription, oldFilters); | 
|  198     delete subscription.oldFilters; |  | 
|  199   }, |  181   }, | 
|  200  |  182  | 
|  201   /** |  183   /** | 
|  202    * Adds a user-defined filter to the list |  184    * Adds a user-defined filter to the list | 
|  203    * @param {Filter} filter |  185    * @param {Filter} filter | 
|  204    * @param {SpecialSubscription} [subscription] |  186    * @param {SpecialSubscription} [subscription] | 
|  205    *   particular group that the filter should be added to |  187    *   particular group that the filter should be added to | 
|  206    * @param {number} [position] |  188    * @param {number} [position] | 
|  207    *   position within the subscription at which the filter should be added |  189    *   position within the subscription at which the filter should be added | 
|  208    */ |  190    */ | 
|  209   addFilter(filter, subscription, position) |  191   addFilter(filter, subscription, position) | 
|  210   { |  192   { | 
|  211     if (!subscription) |  193     if (!subscription) | 
|  212     { |  194     { | 
|  213       if (filter.subscriptions.some(s => s instanceof SpecialSubscription && |  195       for (let currentSubscription of filter.subscriptions()) | 
|  214                                          !s.disabled)) |  196       { | 
|  215       { |  197         if (currentSubscription instanceof SpecialSubscription && | 
|  216         return;   // No need to add |  198             !currentSubscription.disabled) | 
 |  199         { | 
 |  200           return;   // No need to add | 
 |  201         } | 
|  217       } |  202       } | 
|  218       subscription = FilterStorage.getGroupForFilter(filter); |  203       subscription = FilterStorage.getGroupForFilter(filter); | 
|  219     } |  204     } | 
|  220     if (!subscription) |  205     if (!subscription) | 
|  221     { |  206     { | 
|  222       // No group for this filter exists, create one |  207       // No group for this filter exists, create one | 
|  223       subscription = SpecialSubscription.createForFilter(filter); |  208       subscription = SpecialSubscription.createForFilter(filter); | 
|  224       this.addSubscription(subscription); |  209       this.addSubscription(subscription); | 
|  225       return; |  210       return; | 
|  226     } |  211     } | 
|  227  |  212  | 
|  228     if (typeof position == "undefined") |  213     if (typeof position == "undefined") | 
|  229       position = subscription.filters.length; |  214       position = subscription.filters.length; | 
|  230  |  215  | 
|  231     if (filter.subscriptions.indexOf(subscription) < 0) |  216     filter.addSubscription(subscription); | 
|  232       filter.subscriptions.push(subscription); |  | 
|  233     subscription.filters.splice(position, 0, filter); |  217     subscription.filters.splice(position, 0, filter); | 
|  234     FilterNotifier.triggerListeners("filter.added", filter, subscription, |  218     filterNotifier.emit("filter.added", filter, subscription, position); | 
|  235                                     position); |  | 
|  236   }, |  219   }, | 
|  237  |  220  | 
|  238   /** |  221   /** | 
|  239    * Removes a user-defined filter from the list |  222    * Removes a user-defined filter from the list | 
|  240    * @param {Filter} filter |  223    * @param {Filter} filter | 
|  241    * @param {SpecialSubscription} [subscription] a particular filter group that |  224    * @param {SpecialSubscription} [subscription] a particular filter group that | 
|  242    *      the filter should be removed from (if ommited will be removed from all |  225    *      the filter should be removed from (if ommited will be removed from all | 
|  243    *      subscriptions) |  226    *      subscriptions) | 
|  244    * @param {number} [position]  position inside the filter group at which the |  227    * @param {number} [position]  position inside the filter group at which the | 
|  245    *      filter should be removed (if ommited all instances will be removed) |  228    *      filter should be removed (if ommited all instances will be removed) | 
|  246    */ |  229    */ | 
|  247   removeFilter(filter, subscription, position) |  230   removeFilter(filter, subscription, position) | 
|  248   { |  231   { | 
|  249     let subscriptions = ( |  232     let subscriptions = ( | 
|  250       subscription ? [subscription] : filter.subscriptions.slice() |  233       subscription ? [subscription] : filter.subscriptions() | 
|  251     ); |  234     ); | 
|  252     for (let i = 0; i < subscriptions.length; i++) |  235     for (let currentSubscription of subscriptions) | 
|  253     { |  236     { | 
|  254       let currentSubscription = subscriptions[i]; |  | 
|  255       if (currentSubscription instanceof SpecialSubscription) |  237       if (currentSubscription instanceof SpecialSubscription) | 
|  256       { |  238       { | 
|  257         let positions = []; |  239         let positions = []; | 
|  258         if (typeof position == "undefined") |  240         if (typeof position == "undefined") | 
|  259         { |  241         { | 
|  260           let index = -1; |  242           let index = -1; | 
|  261           do |  243           do | 
|  262           { |  244           { | 
|  263             index = currentSubscription.filters.indexOf(filter, index + 1); |  245             index = currentSubscription.filters.indexOf(filter, index + 1); | 
|  264             if (index >= 0) |  246             if (index >= 0) | 
|  265               positions.push(index); |  247               positions.push(index); | 
|  266           } while (index >= 0); |  248           } while (index >= 0); | 
|  267         } |  249         } | 
|  268         else |  250         else | 
|  269           positions.push(position); |  251           positions.push(position); | 
|  270  |  252  | 
|  271         for (let j = positions.length - 1; j >= 0; j--) |  253         for (let j = positions.length - 1; j >= 0; j--) | 
|  272         { |  254         { | 
|  273           let currentPosition = positions[j]; |  255           let currentPosition = positions[j]; | 
|  274           if (currentSubscription.filters[currentPosition] == filter) |  256           if (currentSubscription.filters[currentPosition] == filter) | 
|  275           { |  257           { | 
|  276             currentSubscription.filters.splice(currentPosition, 1); |  258             currentSubscription.filters.splice(currentPosition, 1); | 
|  277             if (currentSubscription.filters.indexOf(filter) < 0) |  259             if (currentSubscription.filters.indexOf(filter) < 0) | 
|  278             { |  260               filter.removeSubscription(currentSubscription); | 
|  279               let index = filter.subscriptions.indexOf(currentSubscription); |  261             filterNotifier.emit("filter.removed", filter, currentSubscription, | 
|  280               if (index >= 0) |  262                                 currentPosition); | 
|  281                 filter.subscriptions.splice(index, 1); |  | 
|  282             } |  | 
|  283             FilterNotifier.triggerListeners( |  | 
|  284               "filter.removed", filter, currentSubscription, currentPosition |  | 
|  285             ); |  | 
|  286           } |  263           } | 
|  287         } |  264         } | 
|  288       } |  265       } | 
|  289     } |  266     } | 
|  290   }, |  267   }, | 
|  291  |  268  | 
|  292   /** |  269   /** | 
|  293    * Moves a user-defined filter to a new position |  270    * Moves a user-defined filter to a new position | 
|  294    * @param {Filter} filter |  271    * @param {Filter} filter | 
|  295    * @param {SpecialSubscription} subscription filter group where the filter is |  272    * @param {SpecialSubscription} subscription filter group where the filter is | 
|  296    *                                           located |  273    *                                           located | 
|  297    * @param {number} oldPosition current position of the filter |  274    * @param {number} oldPosition current position of the filter | 
|  298    * @param {number} newPosition new position of the filter |  275    * @param {number} newPosition new position of the filter | 
|  299    */ |  276    */ | 
|  300   moveFilter(filter, subscription, oldPosition, newPosition) |  277   moveFilter(filter, subscription, oldPosition, newPosition) | 
|  301   { |  278   { | 
|  302     if (!(subscription instanceof SpecialSubscription) || |  279     if (!(subscription instanceof SpecialSubscription) || | 
|  303         subscription.filters[oldPosition] != filter) |  280         subscription.filters[oldPosition] != filter) | 
|  304     { |  281     { | 
|  305       return; |  282       return; | 
|  306     } |  283     } | 
|  307  |  284  | 
|  308     newPosition = Math.min(Math.max(newPosition, 0), |  285     newPosition = Math.min(Math.max(newPosition, 0), | 
|  309                            subscription.filters.length - 1); |  286                            subscription.filters.length - 1); | 
|  310     if (oldPosition == newPosition) |  287     if (oldPosition == newPosition) | 
|  311       return; |  288       return; | 
|  312  |  289  | 
|  313     subscription.filters.splice(oldPosition, 1); |  290     subscription.filters.splice(oldPosition, 1); | 
|  314     subscription.filters.splice(newPosition, 0, filter); |  291     subscription.filters.splice(newPosition, 0, filter); | 
|  315     FilterNotifier.triggerListeners("filter.moved", filter, subscription, |  292     filterNotifier.emit("filter.moved", filter, subscription, oldPosition, | 
|  316                                     oldPosition, newPosition); |  293                         newPosition); | 
|  317   }, |  294   }, | 
|  318  |  295  | 
|  319   /** |  296   /** | 
|  320    * Increases the hit count for a filter by one |  297    * Increases the hit count for a filter by one | 
|  321    * @param {Filter} filter |  298    * @param {Filter} filter | 
|  322    */ |  299    */ | 
|  323   increaseHitCount(filter) |  300   increaseHitCount(filter) | 
|  324   { |  301   { | 
|  325     if (!Prefs.savestats || !(filter instanceof ActiveFilter)) |  302     if (!Prefs.savestats || !(filter instanceof ActiveFilter)) | 
|  326       return; |  303       return; | 
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  366     return line => |  343     return line => | 
|  367     { |  344     { | 
|  368       parser.process(line); |  345       parser.process(line); | 
|  369       if (line === null) |  346       if (line === null) | 
|  370       { |  347       { | 
|  371         let knownSubscriptions = new Map(); |  348         let knownSubscriptions = new Map(); | 
|  372         for (let subscription of parser.subscriptions) |  349         for (let subscription of parser.subscriptions) | 
|  373           knownSubscriptions.set(subscription.url, subscription); |  350           knownSubscriptions.set(subscription.url, subscription); | 
|  374  |  351  | 
|  375         this.fileProperties = parser.fileProperties; |  352         this.fileProperties = parser.fileProperties; | 
|  376         this.subscriptions = parser.subscriptions; |  | 
|  377         this.knownSubscriptions = knownSubscriptions; |  353         this.knownSubscriptions = knownSubscriptions; | 
|  378         Filter.knownFilters = parser.knownFilters; |  354         Filter.knownFilters = parser.knownFilters; | 
|  379         Subscription.knownSubscriptions = parser.knownSubscriptions; |  355         Subscription.knownSubscriptions = parser.knownSubscriptions; | 
|  380  |  356  | 
|  381         if (!silent) |  357         if (!silent) | 
|  382           FilterNotifier.triggerListeners("load"); |  358           filterNotifier.emit("load"); | 
|  383       } |  359       } | 
|  384     }; |  360     }; | 
|  385   }, |  361   }, | 
|  386  |  362  | 
|  387   /** |  363   /** | 
|  388    * Loads all subscriptions from the disk. |  364    * Loads all subscriptions from the disk. | 
|  389    * @return {Promise} promise resolved or rejected when loading is complete |  365    * @return {Promise} promise resolved or rejected when loading is complete | 
|  390    */ |  366    */ | 
|  391   loadFromDisk() |  367   loadFromDisk() | 
|  392   { |  368   { | 
|  393     let tryBackup = backupIndex => |  369     let tryBackup = backupIndex => | 
|  394     { |  370     { | 
|  395       return this.restoreBackup(backupIndex, true).then(() => |  371       return this.restoreBackup(backupIndex, true).then(() => | 
|  396       { |  372       { | 
|  397         if (this.subscriptions.length == 0) |  373         if (this.knownSubscriptions.size == 0) | 
|  398           return tryBackup(backupIndex + 1); |  374           return tryBackup(backupIndex + 1); | 
|  399       }).catch(error => |  375       }).catch(error => | 
|  400       { |  376       { | 
|  401         // Give up |  377         // Give up | 
|  402       }); |  378       }); | 
|  403     }; |  379     }; | 
|  404  |  380  | 
|  405     return IO.statFile(this.sourceFile).then(statData => |  381     return IO.statFile(this.sourceFile).then(statData => | 
|  406     { |  382     { | 
|  407       if (!statData.exists) |  383       if (!statData.exists) | 
|  408       { |  384       { | 
|  409         this.firstRun = true; |  385         this.firstRun = true; | 
|  410         return; |  386         return; | 
|  411       } |  387       } | 
|  412  |  388  | 
|  413       let parser = this.importData(true); |  389       let parser = this.importData(true); | 
|  414       return IO.readFromFile(this.sourceFile, parser).then(() => |  390       return IO.readFromFile(this.sourceFile, parser).then(() => | 
|  415       { |  391       { | 
|  416         parser(null); |  392         parser(null); | 
|  417         if (this.subscriptions.length == 0) |  393         if (this.knownSubscriptions.size == 0) | 
|  418         { |  394         { | 
|  419           // No filter subscriptions in the file, this isn't right. |  395           // No filter subscriptions in the file, this isn't right. | 
|  420           throw new Error("No data in the file"); |  396           throw new Error("No data in the file"); | 
|  421         } |  397         } | 
|  422       }); |  398       }); | 
|  423     }).catch(error => |  399     }).catch(error => | 
|  424     { |  400     { | 
|  425       Cu.reportError(error); |  401       Cu.reportError(error); | 
|  426       return tryBackup(1); |  402       return tryBackup(1); | 
|  427     }).then(() => |  403     }).then(() => | 
|  428     { |  404     { | 
|  429       this.initialized = true; |  405       this.initialized = true; | 
|  430       FilterNotifier.triggerListeners("load"); |  406       filterNotifier.emit("load"); | 
|  431     }); |  407     }); | 
|  432   }, |  408   }, | 
|  433  |  409  | 
|  434   /** |  410   /** | 
|  435    * Constructs the file name for a patterns.ini backup. |  411    * Constructs the file name for a patterns.ini backup. | 
|  436    * @param {number} backupIndex |  412    * @param {number} backupIndex | 
|  437    *    number of the backup file (1 being the most recent) |  413    *    number of the backup file (1 being the most recent) | 
|  438    * @return {string} backup file name |  414    * @return {string} backup file name | 
|  439    */ |  415    */ | 
|  440   getBackupName(backupIndex) |  416   getBackupName(backupIndex) | 
| (...skipping 20 matching lines...) Expand all  Loading... | 
|  461       return this.saveToDisk(); |  437       return this.saveToDisk(); | 
|  462     }); |  438     }); | 
|  463   }, |  439   }, | 
|  464  |  440  | 
|  465   /** |  441   /** | 
|  466    * Generator serializing filter data and yielding it line by line. |  442    * Generator serializing filter data and yielding it line by line. | 
|  467    */ |  443    */ | 
|  468   *exportData() |  444   *exportData() | 
|  469   { |  445   { | 
|  470     // Do not persist external subscriptions |  446     // Do not persist external subscriptions | 
|  471     let subscriptions = this.subscriptions.filter( |  447     let subscriptions = []; | 
|  472       s => !(s instanceof ExternalSubscription) && |  448     for (let subscription of this.subscriptions()) | 
|  473            !(s instanceof SpecialSubscription && s.filters.length == 0) |  449     { | 
|  474     ); |  450       if (!(subscription instanceof ExternalSubscription) && | 
 |  451           !(subscription instanceof SpecialSubscription && | 
 |  452             subscription.filters.length == 0)) | 
 |  453       { | 
 |  454         subscriptions.push(subscription); | 
 |  455       } | 
 |  456     } | 
|  475  |  457  | 
|  476     yield "# Adblock Plus preferences"; |  458     yield "# Adblock Plus preferences"; | 
|  477     yield "version=" + formatVersion; |  459     yield "version=" + formatVersion; | 
|  478  |  460  | 
|  479     let saved = new Set(); |  461     let saved = new Set(); | 
|  480     let buf = []; |  462     let buf = []; | 
|  481  |  463  | 
|  482     // Save subscriptions |  464     // Save subscriptions | 
|  483     for (let subscription of subscriptions) |  465     for (let subscription of subscriptions) | 
|  484     { |  466     { | 
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  589       return renameBackup(Prefs.patternsbackups - 1); |  571       return renameBackup(Prefs.patternsbackups - 1); | 
|  590     }).catch(error => |  572     }).catch(error => | 
|  591     { |  573     { | 
|  592       // Errors during backup creation shouldn't prevent writing filters. |  574       // Errors during backup creation shouldn't prevent writing filters. | 
|  593       Cu.reportError(error); |  575       Cu.reportError(error); | 
|  594     }).then(() => |  576     }).then(() => | 
|  595     { |  577     { | 
|  596       return IO.writeToFile(this.sourceFile, this.exportData()); |  578       return IO.writeToFile(this.sourceFile, this.exportData()); | 
|  597     }).then(() => |  579     }).then(() => | 
|  598     { |  580     { | 
|  599       FilterNotifier.triggerListeners("save"); |  581       filterNotifier.emit("save"); | 
|  600     }).catch(error => |  582     }).catch(error => | 
|  601     { |  583     { | 
|  602       // If saving failed, report error but continue - we still have to process |  584       // If saving failed, report error but continue - we still have to process | 
|  603       // flags. |  585       // flags. | 
|  604       Cu.reportError(error); |  586       Cu.reportError(error); | 
|  605     }).then(() => |  587     }).then(() => | 
|  606     { |  588     { | 
|  607       this._saving = false; |  589       this._saving = false; | 
|  608       if (this._needsSave) |  590       if (this._needsSave) | 
|  609       { |  591       { | 
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  656  * Joins subscription's filters to the subscription without any notifications. |  638  * Joins subscription's filters to the subscription without any notifications. | 
|  657  * @param {Subscription} subscription |  639  * @param {Subscription} subscription | 
|  658  *   filter subscription that should be connected to its filters |  640  *   filter subscription that should be connected to its filters | 
|  659  */ |  641  */ | 
|  660 function addSubscriptionFilters(subscription) |  642 function addSubscriptionFilters(subscription) | 
|  661 { |  643 { | 
|  662   if (!FilterStorage.knownSubscriptions.has(subscription.url)) |  644   if (!FilterStorage.knownSubscriptions.has(subscription.url)) | 
|  663     return; |  645     return; | 
|  664  |  646  | 
|  665   for (let filter of subscription.filters) |  647   for (let filter of subscription.filters) | 
|  666     filter.subscriptions.push(subscription); |  648     filter.addSubscription(subscription); | 
|  667 } |  649 } | 
|  668  |  650  | 
|  669 /** |  651 /** | 
|  670  * Removes subscription's filters from the subscription without any |  652  * Removes subscription's filters from the subscription without any | 
|  671  * notifications. |  653  * notifications. | 
|  672  * @param {Subscription} subscription filter subscription to be removed |  654  * @param {Subscription} subscription filter subscription to be removed | 
|  673  */ |  655  */ | 
|  674 function removeSubscriptionFilters(subscription) |  656 function removeSubscriptionFilters(subscription) | 
|  675 { |  657 { | 
|  676   if (!FilterStorage.knownSubscriptions.has(subscription.url)) |  658   if (!FilterStorage.knownSubscriptions.has(subscription.url)) | 
|  677     return; |  659     return; | 
|  678  |  660  | 
|  679   for (let filter of subscription.filters) |  661   for (let filter of subscription.filters) | 
|  680   { |  662     filter.removeSubscription(subscription); | 
|  681     let i = filter.subscriptions.indexOf(subscription); |  | 
|  682     if (i >= 0) |  | 
|  683       filter.subscriptions.splice(i, 1); |  | 
|  684   } |  | 
|  685 } |  663 } | 
|  686  |  | 
|  687 /** |  | 
|  688  * Listener returned by FilterStorage.importData(), parses filter data. |  | 
|  689  * @constructor |  | 
|  690  */ |  | 
|  691 function INIParser() |  | 
|  692 { |  | 
|  693   this.fileProperties = this.curObj = {}; |  | 
|  694   this.subscriptions = []; |  | 
|  695   this.knownFilters = new Map(); |  | 
|  696   this.knownSubscriptions = new Map(); |  | 
|  697 } |  | 
|  698 INIParser.prototype = |  | 
|  699 { |  | 
|  700   linesProcessed: 0, |  | 
|  701   subscriptions: null, |  | 
|  702   knownFilters: null, |  | 
|  703   knownSubscriptions: null, |  | 
|  704   wantObj: true, |  | 
|  705   fileProperties: null, |  | 
|  706   curObj: null, |  | 
|  707   curSection: null, |  | 
|  708  |  | 
|  709   process(val) |  | 
|  710   { |  | 
|  711     let origKnownFilters = Filter.knownFilters; |  | 
|  712     Filter.knownFilters = this.knownFilters; |  | 
|  713     let origKnownSubscriptions = Subscription.knownSubscriptions; |  | 
|  714     Subscription.knownSubscriptions = this.knownSubscriptions; |  | 
|  715     let match; |  | 
|  716     try |  | 
|  717     { |  | 
|  718       if (this.wantObj === true && (match = /^(\w+)=(.*)$/.exec(val))) |  | 
|  719         this.curObj[match[1]] = match[2]; |  | 
|  720       else if (val === null || (match = /^\s*\[(.+)\]\s*$/.exec(val))) |  | 
|  721       { |  | 
|  722         if (this.curObj) |  | 
|  723         { |  | 
|  724           // Process current object before going to next section |  | 
|  725           switch (this.curSection) |  | 
|  726           { |  | 
|  727             case "filter": |  | 
|  728               if ("text" in this.curObj) |  | 
|  729                 Filter.fromObject(this.curObj); |  | 
|  730               break; |  | 
|  731             case "subscription": { |  | 
|  732               let subscription = Subscription.fromObject(this.curObj); |  | 
|  733               if (subscription) |  | 
|  734                 this.subscriptions.push(subscription); |  | 
|  735               break; |  | 
|  736             } |  | 
|  737             case "subscription filters": |  | 
|  738               if (this.subscriptions.length) |  | 
|  739               { |  | 
|  740                 let subscription = this.subscriptions[ |  | 
|  741                   this.subscriptions.length - 1 |  | 
|  742                 ]; |  | 
|  743                 for (let text of this.curObj) |  | 
|  744                 { |  | 
|  745                   let filter = Filter.fromText(text); |  | 
|  746                   subscription.filters.push(filter); |  | 
|  747                   filter.subscriptions.push(subscription); |  | 
|  748                 } |  | 
|  749               } |  | 
|  750               break; |  | 
|  751           } |  | 
|  752         } |  | 
|  753  |  | 
|  754         if (val === null) |  | 
|  755           return; |  | 
|  756  |  | 
|  757         this.curSection = match[1].toLowerCase(); |  | 
|  758         switch (this.curSection) |  | 
|  759         { |  | 
|  760           case "filter": |  | 
|  761           case "subscription": |  | 
|  762             this.wantObj = true; |  | 
|  763             this.curObj = {}; |  | 
|  764             break; |  | 
|  765           case "subscription filters": |  | 
|  766             this.wantObj = false; |  | 
|  767             this.curObj = []; |  | 
|  768             break; |  | 
|  769           default: |  | 
|  770             this.wantObj = null; |  | 
|  771             this.curObj = null; |  | 
|  772         } |  | 
|  773       } |  | 
|  774       else if (this.wantObj === false && val) |  | 
|  775         this.curObj.push(val.replace(/\\\[/g, "[")); |  | 
|  776     } |  | 
|  777     finally |  | 
|  778     { |  | 
|  779       Filter.knownFilters = origKnownFilters; |  | 
|  780       Subscription.knownSubscriptions = origKnownSubscriptions; |  | 
|  781     } |  | 
|  782   } |  | 
|  783 }; |  | 
| LEFT | RIGHT |