| 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-2016 Eyeo GmbH | 3  * Copyright (C) 2006-2016 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"; | 
|  | 19 | 
| 18 /** | 20 /** | 
| 19  * @fileOverview Definition of Filter class and its subclasses. | 21  * @fileOverview Definition of Filter class and its subclasses. | 
| 20  */ | 22  */ | 
| 21 | 23 | 
| 22 let {FilterNotifier} = require("filterNotifier"); | 24 const {FilterNotifier} = require("filterNotifier"); | 
| 23 let {extend} = require("coreUtils"); | 25 const {extend} = require("coreUtils"); | 
| 24 let {filterToRegExp} = require("common"); | 26 const {filterToRegExp} = require("common"); | 
| 25 | 27 | 
| 26 /** | 28 /** | 
| 27  * Abstract base class for filters | 29  * Abstract base class for filters | 
| 28  * | 30  * | 
| 29  * @param {String} text   string representation of the filter | 31  * @param {string} text   string representation of the filter | 
| 30  * @constructor | 32  * @constructor | 
| 31  */ | 33  */ | 
| 32 function Filter(text) | 34 function Filter(text) | 
| 33 { | 35 { | 
| 34   this.text = text; | 36   this.text = text; | 
| 35   this.subscriptions = []; | 37   this.subscriptions = []; | 
| 36 } | 38 } | 
| 37 exports.Filter = Filter; | 39 exports.Filter = Filter; | 
| 38 | 40 | 
| 39 Filter.prototype = | 41 Filter.prototype = | 
| 40 { | 42 { | 
| 41   /** | 43   /** | 
| 42    * String representation of the filter | 44    * String representation of the filter | 
| 43    * @type String | 45    * @type {string} | 
| 44    */ | 46    */ | 
| 45   text: null, | 47   text: null, | 
| 46 | 48 | 
| 47   /** | 49   /** | 
| 48    * Filter subscriptions the filter belongs to | 50    * Filter subscriptions the filter belongs to | 
| 49    * @type Subscription[] | 51    * @type {Subscription[]} | 
| 50    */ | 52    */ | 
| 51   subscriptions: null, | 53   subscriptions: null, | 
| 52 | 54 | 
| 53   /** | 55   /** | 
| 54    * Filter type as a string, e.g. "blocking". | 56    * Filter type as a string, e.g. "blocking". | 
| 55    * @type String | 57    * @type {string} | 
| 56    */ | 58    */ | 
| 57   get type() | 59   get type() | 
| 58   { | 60   { | 
| 59     throw new Error("Please define filter type in the subclass"); | 61     throw new Error("Please define filter type in the subclass"); | 
| 60   }, | 62   }, | 
| 61 | 63 | 
| 62   /** | 64   /** | 
| 63    * Serializes the filter to an array of strings for writing out on the disk. | 65    * Serializes the filter to an array of strings for writing out on the disk. | 
| 64    * @param {string[]} buffer  buffer to push the serialization results into | 66    * @param {string[]} buffer  buffer to push the serialization results into | 
| 65    */ | 67    */ | 
| 66   serialize: function(buffer) | 68   serialize(buffer) | 
| 67   { | 69   { | 
| 68     buffer.push("[Filter]"); | 70     buffer.push("[Filter]"); | 
| 69     buffer.push("text=" + this.text); | 71     buffer.push("text=" + this.text); | 
| 70   }, | 72   }, | 
| 71 | 73 | 
| 72   toString: function() | 74   toString() | 
| 73   { | 75   { | 
| 74     return this.text; | 76     return this.text; | 
| 75   } | 77   } | 
| 76 }; | 78 }; | 
| 77 | 79 | 
| 78 /** | 80 /** | 
| 79  * Cache for known filters, maps string representation to filter objects. | 81  * Cache for known filters, maps string representation to filter objects. | 
| 80  * @type Object | 82  * @type {Object} | 
| 81  */ | 83  */ | 
| 82 Filter.knownFilters = Object.create(null); | 84 Filter.knownFilters = Object.create(null); | 
| 83 | 85 | 
| 84 /** | 86 /** | 
| 85  * Regular expression that element hiding filters should match | 87  * Regular expression that element hiding filters should match | 
| 86  * @type RegExp | 88  * @type {RegExp} | 
| 87  */ | 89  */ | 
| 88 Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:
      [$^*]?=[^\(\)"]*)?\))*)|#(.+))$/; | 90 Filter.elemhideRegExp = /^([^/*|@"!]*?)#(@)?(?:([\w-]+|\*)((?:\([\w-]+(?:[$^*]?=
      [^()"]*)?\))*)|#(.+))$/; | 
| 89 /** | 91 /** | 
| 90  * Regular expression that RegExp filters specified as RegExps should match | 92  * Regular expression that RegExp filters specified as RegExps should match | 
| 91  * @type RegExp | 93  * @type {RegExp} | 
| 92  */ | 94  */ | 
| 93 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[
      ^,\s]+)?)*)?$/; | 95 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^,
      \s]+)?)*)?$/; | 
| 94 /** | 96 /** | 
| 95  * Regular expression that options on a RegExp filter should match | 97  * Regular expression that options on a RegExp filter should match | 
| 96  * @type RegExp | 98  * @type {RegExp} | 
| 97  */ | 99  */ | 
| 98 Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/
      ; | 100 Filter.optionsRegExp = /\$(~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^,\s]+)?)*)$/; | 
| 99 | 101 | 
| 100 /** | 102 /** | 
| 101  * Creates a filter of correct type from its text representation - does the basi
      c parsing and | 103  * Creates a filter of correct type from its text representation - | 
| 102  * calls the right constructor then. | 104  * does the basic parsing and calls the right constructor then. | 
| 103  * | 105  * | 
| 104  * @param {String} text   as in Filter() | 106  * @param {string} text   as in Filter() | 
| 105  * @return {Filter} | 107  * @return {Filter} | 
| 106  */ | 108  */ | 
| 107 Filter.fromText = function(text) | 109 Filter.fromText = function(text) | 
| 108 { | 110 { | 
| 109   if (text in Filter.knownFilters) | 111   if (text in Filter.knownFilters) | 
| 110     return Filter.knownFilters[text]; | 112     return Filter.knownFilters[text]; | 
| 111 | 113 | 
| 112   let ret; | 114   let ret; | 
| 113   let match = (text.indexOf("#") >= 0 ? Filter.elemhideRegExp.exec(text) : null)
      ; | 115   let match = (text.includes("#") ? Filter.elemhideRegExp.exec(text) : null); | 
| 114   if (match) | 116   if (match) | 
| 115     ret = ElemHideBase.fromText(text, match[1], !!match[2], match[3], match[4], 
      match[5]); | 117   { | 
|  | 118     ret = ElemHideBase.fromText( | 
|  | 119       text, match[1], !!match[2], match[3], match[4], match[5] | 
|  | 120     ); | 
|  | 121   } | 
| 116   else if (text[0] == "!") | 122   else if (text[0] == "!") | 
| 117     ret = new CommentFilter(text); | 123     ret = new CommentFilter(text); | 
| 118   else | 124   else | 
| 119     ret = RegExpFilter.fromText(text); | 125     ret = RegExpFilter.fromText(text); | 
| 120 | 126 | 
| 121   Filter.knownFilters[ret.text] = ret; | 127   Filter.knownFilters[ret.text] = ret; | 
| 122   return ret; | 128   return ret; | 
| 123 }; | 129 }; | 
| 124 | 130 | 
| 125 /** | 131 /** | 
| 126  * Deserializes a filter | 132  * Deserializes a filter | 
| 127  * | 133  * | 
| 128  * @param {Object}  obj map of serialized properties and their values | 134  * @param {Object}  obj map of serialized properties and their values | 
| 129  * @return {Filter} filter or null if the filter couldn't be created | 135  * @return {Filter} filter or null if the filter couldn't be created | 
| 130  */ | 136  */ | 
| 131 Filter.fromObject = function(obj) | 137 Filter.fromObject = function(obj) | 
| 132 { | 138 { | 
| 133   let ret = Filter.fromText(obj.text); | 139   let ret = Filter.fromText(obj.text); | 
| 134   if (ret instanceof ActiveFilter) | 140   if (ret instanceof ActiveFilter) | 
| 135   { | 141   { | 
| 136     if ("disabled" in obj) | 142     if ("disabled" in obj) | 
| 137       ret._disabled = (obj.disabled == "true"); | 143       ret._disabled = (obj.disabled == "true"); | 
| 138     if ("hitCount" in obj) | 144     if ("hitCount" in obj) | 
| 139       ret._hitCount = parseInt(obj.hitCount) || 0; | 145       ret._hitCount = parseInt(obj.hitCount, 10) || 0; | 
| 140     if ("lastHit" in obj) | 146     if ("lastHit" in obj) | 
| 141       ret._lastHit = parseInt(obj.lastHit) || 0; | 147       ret._lastHit = parseInt(obj.lastHit, 10) || 0; | 
| 142   } | 148   } | 
| 143   return ret; | 149   return ret; | 
| 144 }; | 150 }; | 
| 145 | 151 | 
| 146 /** | 152 /** | 
| 147  * Removes unnecessary whitespaces from filter text, will only return null if | 153  * Removes unnecessary whitespaces from filter text, will only return null if | 
| 148  * the input parameter is null. | 154  * the input parameter is null. | 
|  | 155  * @param {string} text | 
|  | 156  * @return {string} | 
| 149  */ | 157  */ | 
| 150 Filter.normalize = function(/**String*/ text) /**String*/ | 158 Filter.normalize = function(text) | 
| 151 { | 159 { | 
| 152   if (!text) | 160   if (!text) | 
| 153     return text; | 161     return text; | 
| 154 | 162 | 
| 155   // Remove line breaks and such | 163   // Remove line breaks and such | 
| 156   text = text.replace(/[^\S ]/g, ""); | 164   text = text.replace(/[^\S ]/g, ""); | 
| 157 | 165 | 
| 158   if (/^\s*!/.test(text)) | 166   if (/^\s*!/.test(text)) | 
| 159   { | 167   { | 
| 160     // Don't remove spaces inside comments | 168     // Don't remove spaces inside comments | 
| 161     return text.trim(); | 169     return text.trim(); | 
| 162   } | 170   } | 
| 163   else if (Filter.elemhideRegExp.test(text)) | 171   else if (Filter.elemhideRegExp.test(text)) | 
| 164   { | 172   { | 
| 165     // Special treatment for element hiding filters, right side is allowed to co
      ntain spaces | 173     // Special treatment for element hiding filters, right side is | 
| 166     let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text); | 174     // allowed to contain spaces | 
|  | 175     let [, domain, separator, selector] = /^(.*?)(#@?#?)(.*)$/.exec(text); | 
| 167     return domain.replace(/\s/g, "") + separator + selector.trim(); | 176     return domain.replace(/\s/g, "") + separator + selector.trim(); | 
| 168   } | 177   } | 
| 169   else | 178   return text.replace(/\s/g, ""); | 
| 170     return text.replace(/\s/g, ""); |  | 
| 171 }; | 179 }; | 
| 172 | 180 | 
| 173 /** | 181 /** | 
| 174  * @see filterToRegExp | 182  * @see filterToRegExp | 
| 175  */ | 183  */ | 
| 176 Filter.toRegExp = filterToRegExp; | 184 Filter.toRegExp = filterToRegExp; | 
| 177 | 185 | 
| 178 /** | 186 /** | 
| 179  * Class for invalid filters | 187  * Class for invalid filters | 
| 180  * @param {String} text see Filter() | 188  * @param {string} text see Filter() | 
| 181  * @param {String} reason Reason why this filter is invalid | 189  * @param {string} reason Reason why this filter is invalid | 
| 182  * @constructor | 190  * @constructor | 
| 183  * @augments Filter | 191  * @augments Filter | 
| 184  */ | 192  */ | 
| 185 function InvalidFilter(text, reason) | 193 function InvalidFilter(text, reason) | 
| 186 { | 194 { | 
| 187   Filter.call(this, text); | 195   Filter.call(this, text); | 
| 188 | 196 | 
| 189   this.reason = reason; | 197   this.reason = reason; | 
| 190 } | 198 } | 
| 191 exports.InvalidFilter = InvalidFilter; | 199 exports.InvalidFilter = InvalidFilter; | 
| 192 | 200 | 
| 193 InvalidFilter.prototype = extend(Filter, { | 201 InvalidFilter.prototype = extend(Filter, { | 
| 194   type: "invalid", | 202   type: "invalid", | 
| 195 | 203 | 
| 196   /** | 204   /** | 
| 197    * Reason why this filter is invalid | 205    * Reason why this filter is invalid | 
| 198    * @type String | 206    * @type {string} | 
| 199    */ | 207    */ | 
| 200   reason: null, | 208   reason: null, | 
| 201 | 209 | 
| 202   /** | 210   /** | 
| 203    * See Filter.serialize() | 211    * See Filter.serialize() | 
|  | 212    * @param {string[]} buffer  buffer to push the serialization results into | 
| 204    */ | 213    */ | 
| 205   serialize: function(buffer) {} | 214   serialize(buffer) {} | 
| 206 }); | 215 }); | 
| 207 | 216 | 
| 208 /** | 217 /** | 
| 209  * Class for comments | 218  * Class for comments | 
| 210  * @param {String} text see Filter() | 219  * @param {string} text see Filter() | 
| 211  * @constructor | 220  * @constructor | 
| 212  * @augments Filter | 221  * @augments Filter | 
| 213  */ | 222  */ | 
| 214 function CommentFilter(text) | 223 function CommentFilter(text) | 
| 215 { | 224 { | 
| 216   Filter.call(this, text); | 225   Filter.call(this, text); | 
| 217 } | 226 } | 
| 218 exports.CommentFilter = CommentFilter; | 227 exports.CommentFilter = CommentFilter; | 
| 219 | 228 | 
| 220 CommentFilter.prototype = extend(Filter, { | 229 CommentFilter.prototype = extend(Filter, { | 
| 221   type: "comment", | 230   type: "comment", | 
| 222 | 231 | 
| 223   /** | 232   /** | 
| 224    * See Filter.serialize() | 233    * See Filter.serialize() | 
|  | 234    * @param {string[]} buffer  buffer to push the serialization results into | 
| 225    */ | 235    */ | 
| 226   serialize: function(buffer) {} | 236   serialize(buffer) {} | 
| 227 }); | 237 }); | 
| 228 | 238 | 
| 229 /** | 239 /** | 
| 230  * Abstract base class for filters that can get hits | 240  * Abstract base class for filters that can get hits | 
| 231  * @param {String} text see Filter() | 241  * @param {string} text see Filter() | 
| 232  * @param {String} [domains] Domains that the filter is restricted to separated 
      by domainSeparator e.g. "foo.com|bar.com|~baz.com" | 242  * @param {string} [domains] Domains that the filter is restricted to | 
|  | 243  *                  separated by domainSeparator e.g. "foo.com|bar.com|~baz.com" | 
| 233  * @constructor | 244  * @constructor | 
| 234  * @augments Filter | 245  * @augments Filter | 
| 235  */ | 246  */ | 
| 236 function ActiveFilter(text, domains) | 247 function ActiveFilter(text, domains) | 
| 237 { | 248 { | 
| 238   Filter.call(this, text); | 249   Filter.call(this, text); | 
| 239 | 250 | 
| 240   this.domainSource = domains; | 251   this.domainSource = domains; | 
| 241 } | 252 } | 
| 242 exports.ActiveFilter = ActiveFilter; | 253 exports.ActiveFilter = ActiveFilter; | 
| 243 | 254 | 
| 244 ActiveFilter.prototype = extend(Filter, { | 255 ActiveFilter.prototype = extend(Filter, { | 
| 245   _disabled: false, | 256   _disabled: false, | 
| 246   _hitCount: 0, | 257   _hitCount: 0, | 
| 247   _lastHit: 0, | 258   _lastHit: 0, | 
| 248 | 259 | 
| 249   /** | 260   /** | 
| 250    * Defines whether the filter is disabled | 261    * Defines whether the filter is disabled | 
| 251    * @type Boolean | 262    * @type {boolean} | 
| 252    */ | 263    */ | 
| 253   get disabled() | 264   get disabled() | 
| 254   { | 265   { | 
| 255     return this._disabled; | 266     return this._disabled; | 
| 256   }, | 267   }, | 
| 257   set disabled(value) | 268   set disabled(value) | 
| 258   { | 269   { | 
| 259     if (value != this._disabled) | 270     if (value != this._disabled) | 
| 260     { | 271     { | 
| 261       let oldValue = this._disabled; | 272       let oldValue = this._disabled; | 
| 262       this._disabled = value; | 273       this._disabled = value; | 
| 263       FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); | 274       FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); | 
| 264     } | 275     } | 
| 265     return this._disabled; | 276     return this._disabled; | 
| 266   }, | 277   }, | 
| 267 | 278 | 
| 268   /** | 279   /** | 
| 269    * Number of hits on the filter since the last reset | 280    * Number of hits on the filter since the last reset | 
| 270    * @type Number | 281    * @type {number} | 
| 271    */ | 282    */ | 
| 272   get hitCount() | 283   get hitCount() | 
| 273   { | 284   { | 
| 274     return this._hitCount; | 285     return this._hitCount; | 
| 275   }, | 286   }, | 
| 276   set hitCount(value) | 287   set hitCount(value) | 
| 277   { | 288   { | 
| 278     if (value != this._hitCount) | 289     if (value != this._hitCount) | 
| 279     { | 290     { | 
| 280       let oldValue = this._hitCount; | 291       let oldValue = this._hitCount; | 
| 281       this._hitCount = value; | 292       this._hitCount = value; | 
| 282       FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); | 293       FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); | 
| 283     } | 294     } | 
| 284     return this._hitCount; | 295     return this._hitCount; | 
| 285   }, | 296   }, | 
| 286 | 297 | 
| 287   /** | 298   /** | 
| 288    * Last time the filter had a hit (in milliseconds since the beginning of the 
      epoch) | 299    * Last time the filter had a hit (in milliseconds since the | 
| 289    * @type Number | 300    * beginning of the epoch) | 
|  | 301    * @type {number} | 
| 290    */ | 302    */ | 
| 291   get lastHit() | 303   get lastHit() | 
| 292   { | 304   { | 
| 293     return this._lastHit; | 305     return this._lastHit; | 
| 294   }, | 306   }, | 
| 295   set lastHit(value) | 307   set lastHit(value) | 
| 296   { | 308   { | 
| 297     if (value != this._lastHit) | 309     if (value != this._lastHit) | 
| 298     { | 310     { | 
| 299       let oldValue = this._lastHit; | 311       let oldValue = this._lastHit; | 
| 300       this._lastHit = value; | 312       this._lastHit = value; | 
| 301       FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); | 313       FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); | 
| 302     } | 314     } | 
| 303     return this._lastHit; | 315     return this._lastHit; | 
| 304   }, | 316   }, | 
| 305 | 317 | 
| 306   /** | 318   /** | 
| 307    * String that the domains property should be generated from | 319    * String that the domains property should be generated from | 
| 308    * @type String | 320    * @type {string} | 
| 309    */ | 321    */ | 
| 310   domainSource: null, | 322   domainSource: null, | 
| 311 | 323 | 
| 312   /** | 324   /** | 
| 313    * Separator character used in domainSource property, must be overridden by su
      bclasses | 325    * Separator character used in domainSource property, must be | 
| 314    * @type String | 326    * overridden by subclasses | 
|  | 327    * @type {string} | 
| 315    */ | 328    */ | 
| 316   domainSeparator: null, | 329   domainSeparator: null, | 
| 317 | 330 | 
| 318   /** | 331   /** | 
| 319    * Determines whether the trailing dot in domain names isn't important and | 332    * Determines whether the trailing dot in domain names isn't important and | 
| 320    * should be ignored, must be overridden by subclasses. | 333    * should be ignored, must be overridden by subclasses. | 
| 321    * @type Boolean | 334    * @type {boolean} | 
| 322    */ | 335    */ | 
| 323   ignoreTrailingDot: true, | 336   ignoreTrailingDot: true, | 
| 324 | 337 | 
| 325   /** | 338   /** | 
| 326    * Determines whether domainSource is already upper-case, | 339    * Determines whether domainSource is already upper-case, | 
| 327    * can be overridden by subclasses. | 340    * can be overridden by subclasses. | 
| 328    * @type Boolean | 341    * @type {boolean} | 
| 329    */ | 342    */ | 
| 330   domainSourceIsUpperCase: false, | 343   domainSourceIsUpperCase: false, | 
| 331 | 344 | 
| 332   /** | 345   /** | 
| 333    * Map containing domains that this filter should match on/not match on or nul
      l if the filter should match on all domains | 346    * Map containing domains that this filter should match on/not match | 
| 334    * @type Object | 347    * on or null if the filter should match on all domains | 
|  | 348    * @type {Object} | 
| 335    */ | 349    */ | 
| 336   get domains() | 350   get domains() | 
| 337   { | 351   { | 
| 338     // Despite this property being cached, the getter is called | 352     // Despite this property being cached, the getter is called | 
| 339     // several times on Safari, due to WebKit bug 132872 | 353     // several times on Safari, due to WebKit bug 132872 | 
| 340     let prop = Object.getOwnPropertyDescriptor(this, "domains"); | 354     let prop = Object.getOwnPropertyDescriptor(this, "domains"); | 
| 341     if (prop) | 355     if (prop) | 
| 342       return prop.value; | 356       return prop.value; | 
| 343 | 357 | 
| 344     let domains = null; | 358     let domains = null; | 
| 345 | 359 | 
| 346     if (this.domainSource) | 360     if (this.domainSource) | 
| 347     { | 361     { | 
| 348       let source = this.domainSource; | 362       let source = this.domainSource; | 
| 349       if (!this.domainSourceIsUpperCase) { | 363       if (!this.domainSourceIsUpperCase) | 
|  | 364       { | 
| 350         // RegExpFilter already have uppercase domains | 365         // RegExpFilter already have uppercase domains | 
| 351         source = source.toUpperCase(); | 366         source = source.toUpperCase(); | 
| 352       } | 367       } | 
| 353       let list = source.split(this.domainSeparator); | 368       let list = source.split(this.domainSeparator); | 
| 354       if (list.length == 1 && list[0][0] != "~") | 369       if (list.length == 1 && list[0][0] != "~") | 
| 355       { | 370       { | 
| 356         // Fast track for the common one-domain scenario | 371         // Fast track for the common one-domain scenario | 
| 357         domains = Object.create(null); | 372         domains = Object.create(null); | 
| 358         domains[""] = false; | 373         domains[""] = false; | 
| 359         if (this.ignoreTrailingDot) | 374         if (this.ignoreTrailingDot) | 
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 394 | 409 | 
| 395       this.domainSource = null; | 410       this.domainSource = null; | 
| 396     } | 411     } | 
| 397 | 412 | 
| 398     Object.defineProperty(this, "domains", {value: domains, enumerable: true}); | 413     Object.defineProperty(this, "domains", {value: domains, enumerable: true}); | 
| 399     return this.domains; | 414     return this.domains; | 
| 400   }, | 415   }, | 
| 401 | 416 | 
| 402   /** | 417   /** | 
| 403    * Array containing public keys of websites that this filter should apply to | 418    * Array containing public keys of websites that this filter should apply to | 
| 404    * @type string[] | 419    * @type {string[]} | 
| 405    */ | 420    */ | 
| 406   sitekeys: null, | 421   sitekeys: null, | 
| 407 | 422 | 
| 408   /** | 423   /** | 
| 409    * Checks whether this filter is active on a domain. | 424    * Checks whether this filter is active on a domain. | 
| 410    * @param {String} docDomain domain name of the document that loads the URL | 425    * @param {string} docDomain domain name of the document that loads the URL | 
| 411    * @param {String} [sitekey] public key provided by the document | 426    * @param {string} [sitekey] public key provided by the document | 
| 412    * @return {Boolean} true in case of the filter being active | 427    * @return {boolean} true in case of the filter being active | 
| 413    */ | 428    */ | 
| 414   isActiveOnDomain: function(docDomain, sitekey) | 429   isActiveOnDomain(docDomain, sitekey) | 
| 415   { | 430   { | 
| 416     // Sitekeys are case-sensitive so we shouldn't convert them to upper-case to
       avoid false | 431     // Sitekeys are case-sensitive so we shouldn't convert them to | 
| 417     // positives here. Instead we need to change the way filter options are pars
      ed. | 432     // upper-case to avoid false positives here. Instead we need to | 
| 418     if (this.sitekeys && (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase(
      )) < 0)) | 433     // change the way filter options are parsed. | 
|  | 434     if (this.sitekeys && | 
|  | 435         (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase()) < 0)) | 
| 419       return false; | 436       return false; | 
| 420 | 437 | 
| 421     // If no domains are set the rule matches everywhere | 438     // If no domains are set the rule matches everywhere | 
| 422     if (!this.domains) | 439     if (!this.domains) | 
| 423       return true; | 440       return true; | 
| 424 | 441 | 
| 425     // If the document has no host name, match only if the filter isn't restrict
      ed to specific domains | 442     // If the document has no host name, match only if the filter | 
|  | 443     // isn't restricted to specific domains | 
| 426     if (!docDomain) | 444     if (!docDomain) | 
| 427       return this.domains[""]; | 445       return this.domains[""]; | 
| 428 | 446 | 
| 429     if (this.ignoreTrailingDot) | 447     if (this.ignoreTrailingDot) | 
| 430       docDomain = docDomain.replace(/\.+$/, ""); | 448       docDomain = docDomain.replace(/\.+$/, ""); | 
| 431     docDomain = docDomain.toUpperCase(); | 449     docDomain = docDomain.toUpperCase(); | 
| 432 | 450 | 
| 433     while (true) | 451     while (true) | 
| 434     { | 452     { | 
| 435       if (docDomain in this.domains) | 453       if (docDomain in this.domains) | 
| 436         return this.domains[docDomain]; | 454         return this.domains[docDomain]; | 
| 437 | 455 | 
| 438       let nextDot = docDomain.indexOf("."); | 456       let nextDot = docDomain.indexOf("."); | 
| 439       if (nextDot < 0) | 457       if (nextDot < 0) | 
| 440         break; | 458         break; | 
| 441       docDomain = docDomain.substr(nextDot + 1); | 459       docDomain = docDomain.substr(nextDot + 1); | 
| 442     } | 460     } | 
| 443     return this.domains[""]; | 461     return this.domains[""]; | 
| 444   }, | 462   }, | 
| 445 | 463 | 
| 446   /** | 464   /** | 
| 447    * Checks whether this filter is active only on a domain and its subdomains. | 465    * Checks whether this filter is active only on a domain and its subdomains. | 
|  | 466    * @param {string} docDomain | 
|  | 467    * @return {boolean} | 
| 448    */ | 468    */ | 
| 449   isActiveOnlyOnDomain: function(/**String*/ docDomain) /**Boolean*/ | 469   isActiveOnlyOnDomain(docDomain) | 
| 450   { | 470   { | 
| 451     if (!docDomain || !this.domains || this.domains[""]) | 471     if (!docDomain || !this.domains || this.domains[""]) | 
| 452       return false; | 472       return false; | 
| 453 | 473 | 
| 454     if (this.ignoreTrailingDot) | 474     if (this.ignoreTrailingDot) | 
| 455       docDomain = docDomain.replace(/\.+$/, ""); | 475       docDomain = docDomain.replace(/\.+$/, ""); | 
| 456     docDomain = docDomain.toUpperCase(); | 476     docDomain = docDomain.toUpperCase(); | 
| 457 | 477 | 
| 458     for (let domain in this.domains) | 478     for (let domain in this.domains) | 
| 459       if (this.domains[domain] && domain != docDomain && (domain.length <= docDo
      main.length || domain.indexOf("." + docDomain) != domain.length - docDomain.leng
      th - 1)) | 479     { | 
| 460         return false; | 480       if (this.domains[domain] && domain != docDomain) | 
|  | 481       { | 
|  | 482         if (domain.length <= docDomain.length) | 
|  | 483           return false; | 
|  | 484 | 
|  | 485         let pos = domain.indexOf("." + docDomain); | 
|  | 486         if (pos != domain.length - docDomain.length - 1) | 
|  | 487           return false; | 
|  | 488       } | 
|  | 489     } | 
| 461 | 490 | 
| 462     return true; | 491     return true; | 
| 463   }, | 492   }, | 
| 464 | 493 | 
| 465   /** | 494   /** | 
| 466    * Checks whether this filter is generic or specific | 495    * Checks whether this filter is generic or specific | 
|  | 496    * @return {boolean} | 
| 467    */ | 497    */ | 
| 468   isGeneric: function() /**Boolean*/ | 498   isGeneric() | 
| 469   { | 499   { | 
| 470     return !(this.sitekeys && this.sitekeys.length) && | 500     return !(this.sitekeys && this.sitekeys.length) && | 
| 471             (!this.domains || this.domains[""]); | 501             (!this.domains || this.domains[""]); | 
| 472   }, | 502   }, | 
| 473 | 503 | 
| 474   /** | 504   /** | 
| 475    * See Filter.serialize() | 505    * See Filter.serialize() | 
|  | 506    * @param {string[]} buffer  buffer to push the serialization results into | 
| 476    */ | 507    */ | 
| 477   serialize: function(buffer) | 508   serialize(buffer) | 
| 478   { | 509   { | 
| 479     if (this._disabled || this._hitCount || this._lastHit) | 510     if (this._disabled || this._hitCount || this._lastHit) | 
| 480     { | 511     { | 
| 481       Filter.prototype.serialize.call(this, buffer); | 512       Filter.prototype.serialize.call(this, buffer); | 
| 482       if (this._disabled) | 513       if (this._disabled) | 
| 483         buffer.push("disabled=true"); | 514         buffer.push("disabled=true"); | 
| 484       if (this._hitCount) | 515       if (this._hitCount) | 
| 485         buffer.push("hitCount=" + this._hitCount); | 516         buffer.push("hitCount=" + this._hitCount); | 
| 486       if (this._lastHit) | 517       if (this._lastHit) | 
| 487         buffer.push("lastHit=" + this._lastHit); | 518         buffer.push("lastHit=" + this._lastHit); | 
| 488     } | 519     } | 
| 489   } | 520   } | 
| 490 }); | 521 }); | 
| 491 | 522 | 
| 492 /** | 523 /** | 
| 493  * Abstract base class for RegExp-based filters | 524  * Abstract base class for RegExp-based filters | 
| 494  * @param {String} text see Filter() | 525  * @param {string} text see Filter() | 
| 495  * @param {String} regexpSource filter part that the regular expression should b
      e build from | 526  * @param {string} regexpSource filter part that the regular expression should | 
| 496  * @param {Number} [contentType] Content types the filter applies to, combinatio
      n of values from RegExpFilter.typeMap | 527  *                              be build from | 
| 497  * @param {Boolean} [matchCase] Defines whether the filter should distinguish be
      tween lower and upper case letters | 528  * @param {number} [contentType] Content types the filter applies to, | 
| 498  * @param {String} [domains] Domains that the filter is restricted to, e.g. "foo
      .com|bar.com|~baz.com" | 529  *                               combination of values from RegExpFilter.typeMap | 
| 499  * @param {Boolean} [thirdParty] Defines whether the filter should apply to thir
      d-party or first-party content only | 530  * @param {boolean} [matchCase] Defines whether the filter should distinguish | 
| 500  * @param {String} [sitekeys] Public keys of websites that this filter should ap
      ply to | 531  *                              between lower and upper case letters | 
|  | 532  * @param {string} [domains] Domains that the filter is restricted to, | 
|  | 533  *                           e.g. "foo.com|bar.com|~baz.com" | 
|  | 534  * @param {boolean} [thirdParty] Defines whether the filter should apply to | 
|  | 535  *                               third-party or first-party content only | 
|  | 536  * @param {string} [sitekeys] Public keys of websites that this filter should | 
|  | 537  *                            apply to | 
| 501  * @constructor | 538  * @constructor | 
| 502  * @augments ActiveFilter | 539  * @augments ActiveFilter | 
| 503  */ | 540  */ | 
| 504 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, third
      Party, sitekeys) | 541 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, | 
|  | 542                       thirdParty, sitekeys) | 
| 505 { | 543 { | 
| 506   ActiveFilter.call(this, text, domains, sitekeys); | 544   ActiveFilter.call(this, text, domains, sitekeys); | 
| 507 | 545 | 
| 508   if (contentType != null) | 546   if (contentType != null) | 
| 509     this.contentType = contentType; | 547     this.contentType = contentType; | 
| 510   if (matchCase) | 548   if (matchCase) | 
| 511     this.matchCase = matchCase; | 549     this.matchCase = matchCase; | 
| 512   if (thirdParty != null) | 550   if (thirdParty != null) | 
| 513     this.thirdParty = thirdParty; | 551     this.thirdParty = thirdParty; | 
| 514   if (sitekeys != null) | 552   if (sitekeys != null) | 
| 515     this.sitekeySource = sitekeys; | 553     this.sitekeySource = sitekeys; | 
| 516 | 554 | 
| 517   if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpS
      ource.length - 1] == "/") | 555   if (regexpSource.length >= 2 && | 
|  | 556       regexpSource[0] == "/" && | 
|  | 557       regexpSource[regexpSource.length - 1] == "/") | 
| 518   { | 558   { | 
| 519     // The filter is a regular expression - convert it immediately to catch synt
      ax errors | 559     // The filter is a regular expression - convert it immediately to | 
| 520     let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), thi
      s.matchCase ? "" : "i"); | 560     // catch syntax errors | 
|  | 561     let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), | 
|  | 562                             this.matchCase ? "" : "i"); | 
| 521     Object.defineProperty(this, "regexp", {value: regexp}); | 563     Object.defineProperty(this, "regexp", {value: regexp}); | 
| 522   } | 564   } | 
| 523   else | 565   else | 
| 524   { | 566   { | 
| 525     // No need to convert this filter to regular expression yet, do it on demand | 567     // No need to convert this filter to regular expression yet, do it on demand | 
| 526     this.regexpSource = regexpSource; | 568     this.regexpSource = regexpSource; | 
| 527   } | 569   } | 
| 528 } | 570 } | 
| 529 exports.RegExpFilter = RegExpFilter; | 571 exports.RegExpFilter = RegExpFilter; | 
| 530 | 572 | 
| 531 RegExpFilter.prototype = extend(ActiveFilter, { | 573 RegExpFilter.prototype = extend(ActiveFilter, { | 
| 532   /** | 574   /** | 
| 533    * @see ActiveFilter.domainSourceIsUpperCase | 575    * @see ActiveFilter.domainSourceIsUpperCase | 
| 534    */ | 576    */ | 
| 535   domainSourceIsUpperCase: true, | 577   domainSourceIsUpperCase: true, | 
| 536 | 578 | 
| 537   /** | 579   /** | 
| 538    * Number of filters contained, will always be 1 (required to optimize Matcher
      ). | 580    * Number of filters contained, will always be 1 (required to | 
| 539    * @type Integer | 581    * optimize Matcher). | 
|  | 582    * @type {number} | 
| 540    */ | 583    */ | 
| 541   length: 1, | 584   length: 1, | 
| 542 | 585 | 
| 543   /** | 586   /** | 
| 544    * @see ActiveFilter.domainSeparator | 587    * @see ActiveFilter.domainSeparator | 
| 545    */ | 588    */ | 
| 546   domainSeparator: "|", | 589   domainSeparator: "|", | 
| 547 | 590 | 
| 548   /** | 591   /** | 
| 549    * Expression from which a regular expression should be generated - for delaye
      d creation of the regexp property | 592    * Expression from which a regular expression should be generated - | 
| 550    * @type String | 593    * for delayed creation of the regexp property | 
|  | 594    * @type {string} | 
| 551    */ | 595    */ | 
| 552   regexpSource: null, | 596   regexpSource: null, | 
| 553   /** | 597   /** | 
| 554    * Regular expression to be used when testing against this filter | 598    * Regular expression to be used when testing against this filter | 
| 555    * @type RegExp | 599    * @type {RegExp} | 
| 556    */ | 600    */ | 
| 557   get regexp() | 601   get regexp() | 
| 558   { | 602   { | 
| 559     // Despite this property being cached, the getter is called | 603     // Despite this property being cached, the getter is called | 
| 560     // several times on Safari, due to WebKit bug 132872 | 604     // several times on Safari, due to WebKit bug 132872 | 
| 561     let prop = Object.getOwnPropertyDescriptor(this, "regexp"); | 605     let prop = Object.getOwnPropertyDescriptor(this, "regexp"); | 
| 562     if (prop) | 606     if (prop) | 
| 563       return prop.value; | 607       return prop.value; | 
| 564 | 608 | 
| 565     let source = Filter.toRegExp(this.regexpSource); | 609     let source = Filter.toRegExp(this.regexpSource); | 
| 566     let regexp = new RegExp(source, this.matchCase ? "" : "i"); | 610     let regexp = new RegExp(source, this.matchCase ? "" : "i"); | 
| 567     Object.defineProperty(this, "regexp", {value: regexp}); | 611     Object.defineProperty(this, "regexp", {value: regexp}); | 
| 568     return regexp; | 612     return regexp; | 
| 569   }, | 613   }, | 
| 570   /** | 614   /** | 
| 571    * Content types the filter applies to, combination of values from RegExpFilte
      r.typeMap | 615    * Content types the filter applies to, combination of values from | 
| 572    * @type Number | 616    * RegExpFilter.typeMap | 
|  | 617    * @type {number} | 
| 573    */ | 618    */ | 
| 574   contentType: 0x7FFFFFFF, | 619   contentType: 0x7FFFFFFF, | 
| 575   /** | 620   /** | 
| 576    * Defines whether the filter should distinguish between lower and upper case 
      letters | 621    * Defines whether the filter should distinguish between lower and | 
| 577    * @type Boolean | 622    * upper case letters | 
|  | 623    * @type {boolean} | 
| 578    */ | 624    */ | 
| 579   matchCase: false, | 625   matchCase: false, | 
| 580   /** | 626   /** | 
| 581    * Defines whether the filter should apply to third-party or first-party conte
      nt only. Can be null (apply to all content). | 627    * Defines whether the filter should apply to third-party or | 
| 582    * @type Boolean | 628    * first-party content only. Can be null (apply to all content). | 
|  | 629    * @type {boolean} | 
| 583    */ | 630    */ | 
| 584   thirdParty: null, | 631   thirdParty: null, | 
| 585 | 632 | 
| 586   /** | 633   /** | 
| 587    * String that the sitekey property should be generated from | 634    * String that the sitekey property should be generated from | 
| 588    * @type String | 635    * @type {string} | 
| 589    */ | 636    */ | 
| 590   sitekeySource: null, | 637   sitekeySource: null, | 
| 591 | 638 | 
| 592   /** | 639   /** | 
| 593    * Array containing public keys of websites that this filter should apply to | 640    * Array containing public keys of websites that this filter should apply to | 
| 594    * @type string[] | 641    * @type {string[]} | 
| 595    */ | 642    */ | 
| 596   get sitekeys() | 643   get sitekeys() | 
| 597   { | 644   { | 
| 598     // Despite this property being cached, the getter is called | 645     // Despite this property being cached, the getter is called | 
| 599     // several times on Safari, due to WebKit bug 132872 | 646     // several times on Safari, due to WebKit bug 132872 | 
| 600     let prop = Object.getOwnPropertyDescriptor(this, "sitekeys"); | 647     let prop = Object.getOwnPropertyDescriptor(this, "sitekeys"); | 
| 601     if (prop) | 648     if (prop) | 
| 602       return prop.value; | 649       return prop.value; | 
| 603 | 650 | 
| 604     let sitekeys = null; | 651     let sitekeys = null; | 
| 605 | 652 | 
| 606     if (this.sitekeySource) | 653     if (this.sitekeySource) | 
| 607     { | 654     { | 
| 608       sitekeys = this.sitekeySource.split("|"); | 655       sitekeys = this.sitekeySource.split("|"); | 
| 609       this.sitekeySource = null; | 656       this.sitekeySource = null; | 
| 610     } | 657     } | 
| 611 | 658 | 
| 612     Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true})
      ; | 659     Object.defineProperty( | 
|  | 660       this, "sitekeys", {value: sitekeys, enumerable: true} | 
|  | 661     ); | 
| 613     return this.sitekeys; | 662     return this.sitekeys; | 
| 614   }, | 663   }, | 
| 615 | 664 | 
| 616   /** | 665   /** | 
| 617    * Tests whether the URL matches this filter | 666    * Tests whether the URL matches this filter | 
| 618    * @param {String} location URL to be tested | 667    * @param {string} location URL to be tested | 
| 619    * @param {number} typeMask bitmask of content / request types to match | 668    * @param {number} typeMask bitmask of content / request types to match | 
| 620    * @param {String} docDomain domain name of the document that loads the URL | 669    * @param {string} docDomain domain name of the document that loads the URL | 
| 621    * @param {Boolean} thirdParty should be true if the URL is a third-party requ
      est | 670    * @param {boolean} thirdParty should be true if the URL is a third-party | 
| 622    * @param {String} sitekey public key provided by the document | 671    *                             request | 
| 623    * @return {Boolean} true in case of a match | 672    * @param {string} sitekey public key provided by the document | 
|  | 673    * @return {boolean} true in case of a match | 
| 624    */ | 674    */ | 
| 625   matches: function(location, typeMask, docDomain, thirdParty, sitekey) | 675   matches(location, typeMask, docDomain, thirdParty, sitekey) | 
| 626   { | 676   { | 
| 627     if (this.contentType & typeMask && | 677     if (this.contentType & typeMask && | 
| 628         (this.thirdParty == null || this.thirdParty == thirdParty) && | 678         (this.thirdParty == null || this.thirdParty == thirdParty) && | 
| 629         this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location)) | 679         this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location)) | 
| 630     { |  | 
| 631       return true; | 680       return true; | 
| 632     } |  | 
| 633 |  | 
| 634     return false; | 681     return false; | 
| 635   } | 682   } | 
| 636 }); | 683 }); | 
| 637 | 684 | 
| 638 // Required to optimize Matcher, see also RegExpFilter.prototype.length | 685 // Required to optimize Matcher, see also RegExpFilter.prototype.length | 
| 639 Object.defineProperty(RegExpFilter.prototype, "0", | 686 Object.defineProperty(RegExpFilter.prototype, "0", { | 
| 640 { | 687   get() { return this; } | 
| 641   get: function() { return this; } |  | 
| 642 }); | 688 }); | 
| 643 | 689 | 
| 644 /** | 690 /** | 
| 645  * Creates a RegExp filter from its text representation | 691  * Creates a RegExp filter from its text representation | 
| 646  * @param {String} text   same as in Filter() | 692  * @param {string} text   same as in Filter() | 
|  | 693  * @return {RegExpFilter} | 
| 647  */ | 694  */ | 
| 648 RegExpFilter.fromText = function(text) | 695 RegExpFilter.fromText = function(text) | 
| 649 { | 696 { | 
| 650   let blocking = true; | 697   let blocking = true; | 
| 651   let origText = text; | 698   let origText = text; | 
| 652   if (text.indexOf("@@") == 0) | 699   if (text.indexOf("@@") == 0) | 
| 653   { | 700   { | 
| 654     blocking = false; | 701     blocking = false; | 
| 655     text = text.substr(2); | 702     text = text.substr(2); | 
| 656   } | 703   } | 
| (...skipping 22 matching lines...) Expand all  Loading... | 
| 679       option = option.replace(/-/, "_"); | 726       option = option.replace(/-/, "_"); | 
| 680       if (option in RegExpFilter.typeMap) | 727       if (option in RegExpFilter.typeMap) | 
| 681       { | 728       { | 
| 682         if (contentType == null) | 729         if (contentType == null) | 
| 683           contentType = 0; | 730           contentType = 0; | 
| 684         contentType |= RegExpFilter.typeMap[option]; | 731         contentType |= RegExpFilter.typeMap[option]; | 
| 685       } | 732       } | 
| 686       else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap) | 733       else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap) | 
| 687       { | 734       { | 
| 688         if (contentType == null) | 735         if (contentType == null) | 
| 689           contentType = RegExpFilter.prototype.contentType; | 736           ({contentType} = RegExpFilter.prototype); | 
| 690         contentType &= ~RegExpFilter.typeMap[option.substr(1)]; | 737         contentType &= ~RegExpFilter.typeMap[option.substr(1)]; | 
| 691       } | 738       } | 
| 692       else if (option == "MATCH_CASE") | 739       else if (option == "MATCH_CASE") | 
| 693         matchCase = true; | 740         matchCase = true; | 
| 694       else if (option == "~MATCH_CASE") | 741       else if (option == "~MATCH_CASE") | 
| 695         matchCase = false; | 742         matchCase = false; | 
| 696       else if (option == "DOMAIN" && typeof value != "undefined") | 743       else if (option == "DOMAIN" && typeof value != "undefined") | 
| 697         domains = value; | 744         domains = value; | 
| 698       else if (option == "THIRD_PARTY") | 745       else if (option == "THIRD_PARTY") | 
| 699         thirdParty = true; | 746         thirdParty = true; | 
| 700       else if (option == "~THIRD_PARTY") | 747       else if (option == "~THIRD_PARTY") | 
| 701         thirdParty = false; | 748         thirdParty = false; | 
| 702       else if (option == "COLLAPSE") | 749       else if (option == "COLLAPSE") | 
| 703         collapse = true; | 750         collapse = true; | 
| 704       else if (option == "~COLLAPSE") | 751       else if (option == "~COLLAPSE") | 
| 705         collapse = false; | 752         collapse = false; | 
| 706       else if (option == "SITEKEY" && typeof value != "undefined") | 753       else if (option == "SITEKEY" && typeof value != "undefined") | 
| 707         sitekeys = value; | 754         sitekeys = value; | 
| 708       else | 755       else | 
| 709         return new InvalidFilter(origText, "filter_unknown_option"); | 756         return new InvalidFilter(origText, "filter_unknown_option"); | 
| 710     } | 757     } | 
| 711   } | 758   } | 
| 712 | 759 | 
| 713   try | 760   try | 
| 714   { | 761   { | 
| 715     if (blocking) | 762     if (blocking) | 
| 716       return new BlockingFilter(origText, text, contentType, matchCase, domains,
       thirdParty, sitekeys, collapse); | 763     { | 
| 717     else | 764       return new BlockingFilter(origText, text, contentType, matchCase, domains, | 
| 718       return new WhitelistFilter(origText, text, contentType, matchCase, domains
      , thirdParty, sitekeys); | 765                                 thirdParty, sitekeys, collapse); | 
|  | 766     } | 
|  | 767     return new WhitelistFilter(origText, text, contentType, matchCase, domains, | 
|  | 768                                thirdParty, sitekeys); | 
| 719   } | 769   } | 
| 720   catch (e) | 770   catch (e) | 
| 721   { | 771   { | 
| 722     return new InvalidFilter(origText, "filter_invalid_regexp"); | 772     return new InvalidFilter(origText, "filter_invalid_regexp"); | 
| 723   } | 773   } | 
| 724 }; | 774 }; | 
| 725 | 775 | 
| 726 /** | 776 /** | 
| 727  * Maps type strings like "SCRIPT" or "OBJECT" to bit masks | 777  * Maps type strings like "SCRIPT" or "OBJECT" to bit masks | 
| 728  */ | 778  */ | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
| 754 // DOCUMENT, ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't | 804 // DOCUMENT, ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't | 
| 755 // be there by default | 805 // be there by default | 
| 756 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.DOCUMENT | | 806 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.DOCUMENT | | 
| 757                                         RegExpFilter.typeMap.ELEMHIDE | | 807                                         RegExpFilter.typeMap.ELEMHIDE | | 
| 758                                         RegExpFilter.typeMap.POPUP | | 808                                         RegExpFilter.typeMap.POPUP | | 
| 759                                         RegExpFilter.typeMap.GENERICHIDE | | 809                                         RegExpFilter.typeMap.GENERICHIDE | | 
| 760                                         RegExpFilter.typeMap.GENERICBLOCK); | 810                                         RegExpFilter.typeMap.GENERICBLOCK); | 
| 761 | 811 | 
| 762 /** | 812 /** | 
| 763  * Class for blocking filters | 813  * Class for blocking filters | 
| 764  * @param {String} text see Filter() | 814  * @param {string} text see Filter() | 
| 765  * @param {String} regexpSource see RegExpFilter() | 815  * @param {string} regexpSource see RegExpFilter() | 
| 766  * @param {Number} contentType see RegExpFilter() | 816  * @param {number} contentType see RegExpFilter() | 
| 767  * @param {Boolean} matchCase see RegExpFilter() | 817  * @param {boolean} matchCase see RegExpFilter() | 
| 768  * @param {String} domains see RegExpFilter() | 818  * @param {string} domains see RegExpFilter() | 
| 769  * @param {Boolean} thirdParty see RegExpFilter() | 819  * @param {boolean} thirdParty see RegExpFilter() | 
| 770  * @param {String} sitekeys see RegExpFilter() | 820  * @param {string} sitekeys see RegExpFilter() | 
| 771  * @param {Boolean} collapse  defines whether the filter should collapse blocked
       content, can be null | 821  * @param {boolean} collapse  defines whether the filter should collapse blocked | 
|  | 822  *                            content, can be null | 
| 772  * @constructor | 823  * @constructor | 
| 773  * @augments RegExpFilter | 824  * @augments RegExpFilter | 
| 774  */ | 825  */ | 
| 775 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thi
      rdParty, sitekeys, collapse) | 826 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, | 
|  | 827                         thirdParty, sitekeys, collapse) | 
| 776 { | 828 { | 
| 777   RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t
      hirdParty, sitekeys); | 829   RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, | 
|  | 830                     thirdParty, sitekeys); | 
| 778 | 831 | 
| 779   this.collapse = collapse; | 832   this.collapse = collapse; | 
| 780 } | 833 } | 
| 781 exports.BlockingFilter = BlockingFilter; | 834 exports.BlockingFilter = BlockingFilter; | 
| 782 | 835 | 
| 783 BlockingFilter.prototype = extend(RegExpFilter, { | 836 BlockingFilter.prototype = extend(RegExpFilter, { | 
| 784   type: "blocking", | 837   type: "blocking", | 
| 785 | 838 | 
| 786   /** | 839   /** | 
| 787    * Defines whether the filter should collapse blocked content. Can be null (us
      e the global preference). | 840    * Defines whether the filter should collapse blocked content. | 
| 788    * @type Boolean | 841    * Can be null (use the global preference). | 
|  | 842    * @type {boolean} | 
| 789    */ | 843    */ | 
| 790   collapse: null | 844   collapse: null | 
| 791 }); | 845 }); | 
| 792 | 846 | 
| 793 /** | 847 /** | 
| 794  * Class for whitelist filters | 848  * Class for whitelist filters | 
| 795  * @param {String} text see Filter() | 849  * @param {string} text see Filter() | 
| 796  * @param {String} regexpSource see RegExpFilter() | 850  * @param {string} regexpSource see RegExpFilter() | 
| 797  * @param {Number} contentType see RegExpFilter() | 851  * @param {number} contentType see RegExpFilter() | 
| 798  * @param {Boolean} matchCase see RegExpFilter() | 852  * @param {boolean} matchCase see RegExpFilter() | 
| 799  * @param {String} domains see RegExpFilter() | 853  * @param {string} domains see RegExpFilter() | 
| 800  * @param {Boolean} thirdParty see RegExpFilter() | 854  * @param {boolean} thirdParty see RegExpFilter() | 
| 801  * @param {String} sitekeys see RegExpFilter() | 855  * @param {string} sitekeys see RegExpFilter() | 
| 802  * @constructor | 856  * @constructor | 
| 803  * @augments RegExpFilter | 857  * @augments RegExpFilter | 
| 804  */ | 858  */ | 
| 805 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, th
      irdParty, sitekeys) | 859 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, | 
|  | 860                          thirdParty, sitekeys) | 
| 806 { | 861 { | 
| 807   RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t
      hirdParty, sitekeys); | 862   RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, | 
|  | 863                     thirdParty, sitekeys); | 
| 808 } | 864 } | 
| 809 exports.WhitelistFilter = WhitelistFilter; | 865 exports.WhitelistFilter = WhitelistFilter; | 
| 810 | 866 | 
| 811 WhitelistFilter.prototype = extend(RegExpFilter, { | 867 WhitelistFilter.prototype = extend(RegExpFilter, { | 
| 812   type: "whitelist" | 868   type: "whitelist" | 
| 813 }); | 869 }); | 
| 814 | 870 | 
| 815 /** | 871 /** | 
| 816  * Base class for element hiding filters | 872  * Base class for element hiding filters | 
| 817  * @param {String} text see Filter() | 873  * @param {string} text see Filter() | 
| 818  * @param {String} [domains] Host names or domains the filter should be restrict
      ed to | 874  * @param {string} [domains] Host names or domains the filter should be | 
| 819  * @param {String} selector   CSS selector for the HTML elements that should be 
      hidden | 875  *                           restricted to | 
|  | 876  * @param {string} selector   CSS selector for the HTML elements that should be | 
|  | 877  *                            hidden | 
| 820  * @constructor | 878  * @constructor | 
| 821  * @augments ActiveFilter | 879  * @augments ActiveFilter | 
| 822  */ | 880  */ | 
| 823 function ElemHideBase(text, domains, selector) | 881 function ElemHideBase(text, domains, selector) | 
| 824 { | 882 { | 
| 825   ActiveFilter.call(this, text, domains || null); | 883   ActiveFilter.call(this, text, domains || null); | 
| 826 | 884 | 
| 827   if (domains) | 885   if (domains) | 
| 828     this.selectorDomain = domains.replace(/,~[^,]+/g, "").replace(/^~[^,]+,?/, "
      ").toLowerCase(); | 886   { | 
|  | 887     this.selectorDomain = domains.replace(/,~[^,]+/g, "") | 
|  | 888                                  .replace(/^~[^,]+,?/, "").toLowerCase(); | 
|  | 889   } | 
| 829 | 890 | 
| 830   // Braces are being escaped to prevent CSS rule injection. | 891   // Braces are being escaped to prevent CSS rule injection. | 
| 831   this.selector = selector.replace("{", "\\x7B ").replace("}", "\\x7D "); | 892   this.selector = selector.replace("{", "\\x7B ").replace("}", "\\x7D "); | 
| 832 } | 893 } | 
| 833 exports.ElemHideBase = ElemHideBase; | 894 exports.ElemHideBase = ElemHideBase; | 
| 834 | 895 | 
| 835 ElemHideBase.prototype = extend(ActiveFilter, { | 896 ElemHideBase.prototype = extend(ActiveFilter, { | 
| 836   /** | 897   /** | 
| 837    * @see ActiveFilter.domainSeparator | 898    * @see ActiveFilter.domainSeparator | 
| 838    */ | 899    */ | 
| 839   domainSeparator: ",", | 900   domainSeparator: ",", | 
| 840 | 901 | 
| 841   /** | 902   /** | 
| 842    * @see ActiveFilter.ignoreTrailingDot | 903    * @see ActiveFilter.ignoreTrailingDot | 
| 843    */ | 904    */ | 
| 844   ignoreTrailingDot: false, | 905   ignoreTrailingDot: false, | 
| 845 | 906 | 
| 846   /** | 907   /** | 
| 847    * Host name or domain the filter should be restricted to (can be null for no 
      restriction) | 908    * Host name or domain the filter should be restricted to (can be null for | 
| 848    * @type String | 909    * no restriction) | 
|  | 910    * @type {string} | 
| 849    */ | 911    */ | 
| 850   selectorDomain: null, | 912   selectorDomain: null, | 
| 851   /** | 913   /** | 
| 852    * CSS selector for the HTML elements that should be hidden | 914    * CSS selector for the HTML elements that should be hidden | 
| 853    * @type String | 915    * @type {string} | 
| 854    */ | 916    */ | 
| 855   selector: null | 917   selector: null | 
| 856 }); | 918 }); | 
| 857 | 919 | 
| 858 /** | 920 /** | 
| 859  * Creates an element hiding filter from a pre-parsed text representation | 921  * Creates an element hiding filter from a pre-parsed text representation | 
| 860  * | 922  * | 
| 861  * @param {String} text         same as in Filter() | 923  * @param {string} text         same as in Filter() | 
| 862  * @param {String} domain       domain part of the text representation (can be e
      mpty) | 924  * @param {string} domain       domain part of the text representation | 
| 863  * @param {Boolean} isException exception rule indicator | 925  *                              (can be empty) | 
| 864  * @param {String} tagName      tag name part (can be empty) | 926  * @param {boolean} isException exception rule indicator | 
| 865  * @param {String} attrRules    attribute matching rules (can be empty) | 927  * @param {string} tagName      tag name part (can be empty) | 
| 866  * @param {String} selector     raw CSS selector (can be empty) | 928  * @param {string} attrRules    attribute matching rules (can be empty) | 
| 867  * @return {ElemHideFilter|ElemHideException|ElemHideEmulationFilter|InvalidFilt
      er} | 929  * @param {string} selector     raw CSS selector (can be empty) | 
|  | 930  * @return {ElemHideFilter|ElemHideException| | 
|  | 931  *          ElemHideEmulationFilter|InvalidFilter} | 
| 868  */ | 932  */ | 
| 869 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, 
      selector) | 933 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, | 
|  | 934                                  selector) | 
| 870 { | 935 { | 
| 871   if (!selector) | 936   if (!selector) | 
| 872   { | 937   { | 
| 873     if (tagName == "*") | 938     if (tagName == "*") | 
| 874       tagName = ""; | 939       tagName = ""; | 
| 875 | 940 | 
| 876     let id = null; | 941     let id = null; | 
| 877     let additional = ""; | 942     let additional = ""; | 
| 878     if (attrRules) | 943     if (attrRules) | 
| 879     { | 944     { | 
| 880       attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g); | 945       attrRules = attrRules.match(/\([\w-]+(?:[$^*]?=[^()"]*)?\)/g); | 
| 881       for (let rule of attrRules) | 946       for (let rule of attrRules) | 
| 882       { | 947       { | 
| 883         rule = rule.substr(1, rule.length - 2); | 948         rule = rule.substr(1, rule.length - 2); | 
| 884         let separatorPos = rule.indexOf("="); | 949         let separatorPos = rule.indexOf("="); | 
| 885         if (separatorPos > 0) | 950         if (separatorPos > 0) | 
| 886         { | 951         { | 
| 887           rule = rule.replace(/=/, '="') + '"'; | 952           rule = rule.replace(/=/, '="') + '"'; | 
| 888           additional += "[" + rule + "]"; | 953           additional += "[" + rule + "]"; | 
| 889         } | 954         } | 
| 890         else | 955         else | 
| 891         { | 956         { | 
| 892           if (id) | 957           if (id) | 
| 893             return new InvalidFilter(text, "filter_elemhide_duplicate_id"); | 958             return new InvalidFilter(text, "filter_elemhide_duplicate_id"); | 
| 894 | 959 | 
| 895           id = rule; | 960           id = rule; | 
| 896         } | 961         } | 
| 897       } | 962       } | 
| 898     } | 963     } | 
| 899 | 964 | 
| 900     if (id) | 965     if (id) | 
| 901       selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad
      ditional; | 966       selector = `${tagName}.${id}${additional},${tagName}#${id}${additional}`; | 
| 902     else if (tagName || additional) | 967     else if (tagName || additional) | 
| 903       selector = tagName + additional; | 968       selector = tagName + additional; | 
| 904     else | 969     else | 
| 905       return new InvalidFilter(text, "filter_elemhide_nocriteria"); | 970       return new InvalidFilter(text, "filter_elemhide_nocriteria"); | 
| 906   } | 971   } | 
| 907 | 972 | 
| 908   // We don't allow ElemHide filters which have any empty domains. | 973   // We don't allow ElemHide filters which have any empty domains. | 
| 909   // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that | 974   // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that | 
| 910   // changes this must be changed too. | 975   // changes this must be changed too. | 
| 911   if (domain && /(^|,)~?(,|$)/.test(domain)) | 976   if (domain && /(^|,)~?(,|$)/.test(domain)) | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
| 922       return new InvalidFilter(text, "filter_elemhideemulation_nodomain"); | 987       return new InvalidFilter(text, "filter_elemhideemulation_nodomain"); | 
| 923 | 988 | 
| 924     return new ElemHideEmulationFilter(text, domain, selector); | 989     return new ElemHideEmulationFilter(text, domain, selector); | 
| 925   } | 990   } | 
| 926 | 991 | 
| 927   return new ElemHideFilter(text, domain, selector); | 992   return new ElemHideFilter(text, domain, selector); | 
| 928 }; | 993 }; | 
| 929 | 994 | 
| 930 /** | 995 /** | 
| 931  * Class for element hiding filters | 996  * Class for element hiding filters | 
| 932  * @param {String} text see Filter() | 997  * @param {string} text see Filter() | 
| 933  * @param {String} domains  see ElemHideBase() | 998  * @param {string} domains  see ElemHideBase() | 
| 934  * @param {String} selector see ElemHideBase() | 999  * @param {string} selector see ElemHideBase() | 
| 935  * @constructor | 1000  * @constructor | 
| 936  * @augments ElemHideBase | 1001  * @augments ElemHideBase | 
| 937  */ | 1002  */ | 
| 938 function ElemHideFilter(text, domains, selector) | 1003 function ElemHideFilter(text, domains, selector) | 
| 939 { | 1004 { | 
| 940   ElemHideBase.call(this, text, domains, selector); | 1005   ElemHideBase.call(this, text, domains, selector); | 
| 941 } | 1006 } | 
| 942 exports.ElemHideFilter = ElemHideFilter; | 1007 exports.ElemHideFilter = ElemHideFilter; | 
| 943 | 1008 | 
| 944 ElemHideFilter.prototype = extend(ElemHideBase, { | 1009 ElemHideFilter.prototype = extend(ElemHideBase, { | 
| 945   type: "elemhide" | 1010   type: "elemhide" | 
| 946 }); | 1011 }); | 
| 947 | 1012 | 
| 948 /** | 1013 /** | 
| 949  * Class for element hiding exceptions | 1014  * Class for element hiding exceptions | 
| 950  * @param {String} text see Filter() | 1015  * @param {string} text see Filter() | 
| 951  * @param {String} domains  see ElemHideBase() | 1016  * @param {string} domains  see ElemHideBase() | 
| 952  * @param {String} selector see ElemHideBase() | 1017  * @param {string} selector see ElemHideBase() | 
| 953  * @constructor | 1018  * @constructor | 
| 954  * @augments ElemHideBase | 1019  * @augments ElemHideBase | 
| 955  */ | 1020  */ | 
| 956 function ElemHideException(text, domains, selector) | 1021 function ElemHideException(text, domains, selector) | 
| 957 { | 1022 { | 
| 958   ElemHideBase.call(this, text, domains, selector); | 1023   ElemHideBase.call(this, text, domains, selector); | 
| 959 } | 1024 } | 
| 960 exports.ElemHideException = ElemHideException; | 1025 exports.ElemHideException = ElemHideException; | 
| 961 | 1026 | 
| 962 ElemHideException.prototype = extend(ElemHideBase, { | 1027 ElemHideException.prototype = extend(ElemHideBase, { | 
| 963   type: "elemhideexception" | 1028   type: "elemhideexception" | 
| 964 }); | 1029 }); | 
| 965 | 1030 | 
| 966 /** | 1031 /** | 
| 967  * Class for element hiding emulation filters | 1032  * Class for element hiding emulation filters | 
| 968  * @param {String} text           see Filter() | 1033  * @param {string} text           see Filter() | 
| 969  * @param {String} domains        see ElemHideBase() | 1034  * @param {string} domains        see ElemHideBase() | 
| 970  * @param {String} selector       see ElemHideBase() | 1035  * @param {string} selector       see ElemHideBase() | 
| 971  * @constructor | 1036  * @constructor | 
| 972  * @augments ElemHideBase | 1037  * @augments ElemHideBase | 
| 973  */ | 1038  */ | 
| 974 function ElemHideEmulationFilter(text, domains, selector) | 1039 function ElemHideEmulationFilter(text, domains, selector) | 
| 975 { | 1040 { | 
| 976   ElemHideBase.call(this, text, domains, selector); | 1041   ElemHideBase.call(this, text, domains, selector); | 
| 977 } | 1042 } | 
| 978 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; | 1043 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; | 
| 979 | 1044 | 
| 980 ElemHideEmulationFilter.prototype = extend(ElemHideBase, { | 1045 ElemHideEmulationFilter.prototype = extend(ElemHideBase, { | 
| 981   type: "elemhideemulation" | 1046   type: "elemhideemulation" | 
| 982 }); | 1047 }); | 
| OLD | NEW | 
|---|