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

Side by Side Diff: lib/filterClasses.js

Issue 29375915: Issue 4878 - Start using ESLint for adblockpluscore (Closed)
Patch Set: Rebased. Created Feb. 28, 2017, 3:55 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-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.
Wladimir Palant 2017/03/02 14:06:45 Nit: "does the" belongs on the previous line.
kzar 2017/03/08 12:33:31 Done.
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);
Wladimir Palant 2017/03/02 14:06:47 Please add integration notes to the issue - this i
kzar 2017/03/08 12:33:36 Done.
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
Wladimir Palant 2017/03/02 14:06:45 Nit: "allowed to" belongs on the previous line.
kzar 2017/03/08 12:33:30 Done.
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
Wladimir Palant 2017/03/02 14:06:46 Do we really want to duplicate this description ev
kzar 2017/03/08 12:33:32 Well I've removed those duplicate descriptions but
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"
Wladimir Palant 2017/03/02 14:06:49 As before, I'd suggest avoiding the messy indentat
kzar 2017/03/08 12:33:35 Done.
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)
Wladimir Palant 2017/03/02 14:06:48 Why change the spacing in this description?
kzar 2017/03/08 12:33:30 Done.
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
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)
Wladimir Palant 2017/03/02 14:06:48 Just change this into: if (!domain.endsWith("."
kzar 2017/03/08 12:33:29 Done.
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
Wladimir Palant 2017/03/02 14:06:48 As before, please avoid the messy indentation here
kzar 2017/03/08 12:33:33 Done.
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;
Wladimir Palant 2017/03/02 14:06:45 Since ESLint won't let you keep the braces here, I
kzar 2017/03/08 12:33:35 We can keep the braces now.
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}
Wladimir Palant 2017/03/02 14:06:49 This should be RegExpFilter|InvalidFilter. Or mayb
kzar 2017/03/08 12:33:34 Done.
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
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
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
Wladimir Palant 2017/03/02 14:06:47 Messy indentation as before.
kzar 2017/03/08 12:33:31 Done.
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)
Wladimir Palant 2017/03/02 14:06:46 Messy indentation as before.
kzar 2017/03/08 12:33:32 Done.
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
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 });
OLDNEW

Powered by Google App Engine
This is Rietveld