Left: | ||
Right: |
OLD | NEW |
---|---|
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-present eyeo GmbH | 3 * Copyright (C) 2006-present eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 "use strict"; | 18 "use strict"; |
19 | 19 |
20 /** | 20 /** |
21 * @fileOverview Definition of Filter class and its subclasses. | 21 * @fileOverview Definition of Filter class and its subclasses. |
22 */ | 22 */ |
23 | 23 |
24 const {FilterNotifier} = require("./filterNotifier"); | 24 const {FilterNotifier} = require("./filterNotifier"); |
25 const {extend} = require("./coreUtils"); | |
26 const {filterToRegExp} = require("./common"); | 25 const {filterToRegExp} = require("./common"); |
27 | 26 |
28 /** | 27 /** |
29 * Abstract base class for filters | 28 * Abstract base class for filters |
30 * | |
31 * @param {string} text string representation of the filter | |
32 * @constructor | |
33 */ | 29 */ |
34 function Filter(text) | 30 class Filter |
35 { | |
36 this.text = text; | |
37 this.subscriptions = []; | |
38 } | |
39 exports.Filter = Filter; | |
40 | |
41 Filter.prototype = | |
42 { | 31 { |
43 /** | 32 /** |
44 * String representation of the filter | 33 * @param {string} text string representation of the filter |
45 * @type {string} | |
46 */ | 34 */ |
47 text: null, | 35 constructor(text) |
36 { | |
37 /** | |
Manish Jethani
2018/06/09 13:03:32
For instance properties that are always assigned i
| |
38 * String representation of the filter | |
39 * @type {string} | |
40 */ | |
41 this.text = text; | |
48 | 42 |
49 /** | 43 /** |
50 * Filter subscriptions the filter belongs to | 44 * Filter subscriptions the filter belongs to |
51 * @type {Subscription[]} | 45 * @type {Subscription[]} |
52 */ | 46 */ |
53 subscriptions: null, | 47 this.subscriptions = []; |
48 } | |
54 | 49 |
55 /** | 50 /** |
56 * Filter type as a string, e.g. "blocking". | 51 * Filter type as a string, e.g. "blocking". |
57 * @type {string} | 52 * @type {string} |
58 */ | 53 */ |
59 get type() | 54 get type() |
60 { | 55 { |
61 throw new Error("Please define filter type in the subclass"); | 56 throw new Error("Please define filter type in the subclass"); |
62 }, | 57 } |
63 | 58 |
64 /** | 59 /** |
65 * Serializes the filter to an array of strings for writing out on the disk. | 60 * Serializes the filter to an array of strings for writing out on the disk. |
66 * @param {string[]} buffer buffer to push the serialization results into | 61 * @param {string[]} buffer buffer to push the serialization results into |
67 */ | 62 */ |
68 serialize(buffer) | 63 serialize(buffer) |
69 { | 64 { |
70 buffer.push("[Filter]"); | 65 buffer.push("[Filter]"); |
71 buffer.push("text=" + this.text); | 66 buffer.push("text=" + this.text); |
72 }, | 67 } |
73 | 68 |
74 toString() | 69 toString() |
75 { | 70 { |
76 return this.text; | 71 return this.text; |
77 } | 72 } |
78 }; | 73 } |
74 | |
75 exports.Filter = Filter; | |
79 | 76 |
80 /** | 77 /** |
81 * Cache for known filters, maps string representation to filter objects. | 78 * Cache for known filters, maps string representation to filter objects. |
82 * @type {Map.<string,Filter>} | 79 * @type {Map.<string,Filter>} |
83 */ | 80 */ |
84 Filter.knownFilters = new Map(); | 81 Filter.knownFilters = new Map(); |
85 | 82 |
86 /** | 83 /** |
87 * Regular expression that element hiding filters should match | 84 * Regular expression that element hiding filters should match |
88 * @type {RegExp} | 85 * @type {RegExp} |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
233 return beforeOptions + "$" + options.join(); | 230 return beforeOptions + "$" + options.join(); |
234 }; | 231 }; |
235 | 232 |
236 /** | 233 /** |
237 * @see filterToRegExp | 234 * @see filterToRegExp |
238 */ | 235 */ |
239 Filter.toRegExp = filterToRegExp; | 236 Filter.toRegExp = filterToRegExp; |
240 | 237 |
241 /** | 238 /** |
242 * Class for invalid filters | 239 * Class for invalid filters |
243 * @param {string} text see Filter() | |
244 * @param {string} reason Reason why this filter is invalid | |
245 * @constructor | |
246 * @augments Filter | |
247 */ | 240 */ |
248 function InvalidFilter(text, reason) | 241 class InvalidFilter extends Filter |
249 { | 242 { |
250 Filter.call(this, text); | 243 /** |
244 * @param {string} text see Filter() | |
245 * @param {string} reason Reason why this filter is invalid | |
246 */ | |
247 constructor(text, reason) | |
248 { | |
249 super(text); | |
251 | 250 |
252 this.reason = reason; | 251 /** |
253 } | 252 * Reason why this filter is invalid |
254 exports.InvalidFilter = InvalidFilter; | 253 * @type {string} |
254 */ | |
255 this.reason = reason; | |
256 } | |
255 | 257 |
256 InvalidFilter.prototype = extend(Filter, { | 258 get type() |
Manish Jethani
2018/06/09 13:03:32
This needs to be a getter now.
| |
257 type: "invalid", | 259 { |
258 | 260 return "invalid"; |
259 /** | 261 } |
260 * Reason why this filter is invalid | |
261 * @type {string} | |
262 */ | |
263 reason: null, | |
264 | 262 |
265 /** | 263 /** |
266 * See Filter.serialize() | 264 * See Filter.serialize() |
267 * @inheritdoc | 265 * @inheritdoc |
268 */ | 266 */ |
269 serialize(buffer) {} | 267 serialize(buffer) {} |
270 }); | 268 } |
269 | |
270 exports.InvalidFilter = InvalidFilter; | |
271 | 271 |
272 /** | 272 /** |
273 * Class for comments | 273 * Class for comments |
274 * @param {string} text see Filter() | |
275 * @constructor | |
276 * @augments Filter | |
277 */ | 274 */ |
278 function CommentFilter(text) | 275 class CommentFilter extends Filter |
279 { | 276 { |
280 Filter.call(this, text); | 277 /** |
281 } | 278 * @param {string} text see Filter() |
282 exports.CommentFilter = CommentFilter; | 279 */ |
280 constructor(text) | |
281 { | |
282 super(text); | |
283 } | |
283 | 284 |
284 CommentFilter.prototype = extend(Filter, { | 285 get type() |
285 type: "comment", | 286 { |
287 return "comment"; | |
288 } | |
286 | 289 |
287 /** | 290 /** |
288 * See Filter.serialize() | 291 * See Filter.serialize() |
289 * @inheritdoc | 292 * @inheritdoc |
290 */ | 293 */ |
291 serialize(buffer) {} | 294 serialize(buffer) {} |
292 }); | 295 } |
296 | |
297 exports.CommentFilter = CommentFilter; | |
293 | 298 |
294 /** | 299 /** |
295 * Abstract base class for filters that can get hits | 300 * Abstract base class for filters that can get hits |
296 * @param {string} text | |
297 * see Filter() | |
298 * @param {string} [domains] | |
299 * Domains that the filter is restricted to separated by domainSeparator | |
300 * e.g. "foo.com|bar.com|~baz.com" | |
301 * @constructor | |
302 * @augments Filter | |
303 */ | 301 */ |
304 function ActiveFilter(text, domains) | 302 class ActiveFilter extends Filter |
305 { | 303 { |
306 Filter.call(this, text); | 304 /** |
305 * @param {string} text | |
306 * see Filter() | |
307 * @param {string} [domains] | |
308 * Domains that the filter is restricted to separated by domainSeparator | |
309 * e.g. "foo.com|bar.com|~baz.com" | |
310 */ | |
311 constructor(text, domains) | |
312 { | |
313 super(text); | |
307 | 314 |
308 if (domains) | 315 if (domains) |
309 this.domainSource = domains; | 316 this.domainSource = domains; |
310 } | 317 } |
311 exports.ActiveFilter = ActiveFilter; | |
312 | |
313 ActiveFilter.prototype = extend(Filter, { | |
314 _disabled: false, | |
315 _hitCount: 0, | |
316 _lastHit: 0, | |
317 | 318 |
318 /** | 319 /** |
319 * Defines whether the filter is disabled | 320 * Defines whether the filter is disabled |
320 * @type {boolean} | 321 * @type {boolean} |
321 */ | 322 */ |
322 get disabled() | 323 get disabled() |
323 { | 324 { |
324 return this._disabled; | 325 return this._disabled; |
325 }, | 326 } |
326 set disabled(value) | 327 set disabled(value) |
327 { | 328 { |
328 if (value != this._disabled) | 329 if (value != this._disabled) |
329 { | 330 { |
330 let oldValue = this._disabled; | 331 let oldValue = this._disabled; |
331 this._disabled = value; | 332 this._disabled = value; |
332 FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); | 333 FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); |
333 } | 334 } |
334 return this._disabled; | 335 return this._disabled; |
335 }, | 336 } |
336 | 337 |
337 /** | 338 /** |
338 * Number of hits on the filter since the last reset | 339 * Number of hits on the filter since the last reset |
339 * @type {number} | 340 * @type {number} |
340 */ | 341 */ |
341 get hitCount() | 342 get hitCount() |
342 { | 343 { |
343 return this._hitCount; | 344 return this._hitCount; |
344 }, | 345 } |
345 set hitCount(value) | 346 set hitCount(value) |
346 { | 347 { |
347 if (value != this._hitCount) | 348 if (value != this._hitCount) |
348 { | 349 { |
349 let oldValue = this._hitCount; | 350 let oldValue = this._hitCount; |
350 this._hitCount = value; | 351 this._hitCount = value; |
351 FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); | 352 FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); |
352 } | 353 } |
353 return this._hitCount; | 354 return this._hitCount; |
354 }, | 355 } |
355 | 356 |
356 /** | 357 /** |
357 * Last time the filter had a hit (in milliseconds since the beginning of the | 358 * Last time the filter had a hit (in milliseconds since the beginning of the |
358 * epoch) | 359 * epoch) |
359 * @type {number} | 360 * @type {number} |
360 */ | 361 */ |
361 get lastHit() | 362 get lastHit() |
362 { | 363 { |
363 return this._lastHit; | 364 return this._lastHit; |
364 }, | 365 } |
365 set lastHit(value) | 366 set lastHit(value) |
366 { | 367 { |
367 if (value != this._lastHit) | 368 if (value != this._lastHit) |
368 { | 369 { |
369 let oldValue = this._lastHit; | 370 let oldValue = this._lastHit; |
370 this._lastHit = value; | 371 this._lastHit = value; |
371 FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); | 372 FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); |
372 } | 373 } |
373 return this._lastHit; | 374 return this._lastHit; |
374 }, | 375 } |
375 | |
376 /** | |
377 * String that the domains property should be generated from | |
378 * @type {?string} | |
379 */ | |
380 domainSource: null, | |
381 | |
382 /** | |
383 * Separator character used in domainSource property, must be | |
384 * overridden by subclasses | |
385 * @type {string} | |
386 */ | |
387 domainSeparator: null, | |
388 | |
389 /** | |
390 * Determines whether domainSource is already upper-case, | |
391 * can be overridden by subclasses. | |
392 * @type {boolean} | |
393 */ | |
394 domainSourceIsUpperCase: false, | |
395 | 376 |
396 /** | 377 /** |
397 * Map containing domains that this filter should match on/not match | 378 * Map containing domains that this filter should match on/not match |
398 * on or null if the filter should match on all domains | 379 * on or null if the filter should match on all domains |
399 * @type {?Map.<string,boolean>} | 380 * @type {?Map.<string,boolean>} |
400 */ | 381 */ |
401 get domains() | 382 get domains() |
402 { | 383 { |
403 let domains = null; | 384 let domains = null; |
404 | 385 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
444 } | 425 } |
445 if (domains) | 426 if (domains) |
446 domains.set("", !hasIncludes); | 427 domains.set("", !hasIncludes); |
447 } | 428 } |
448 | 429 |
449 this.domainSource = null; | 430 this.domainSource = null; |
450 } | 431 } |
451 | 432 |
452 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); | 433 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); |
453 return this.domains; | 434 return this.domains; |
454 }, | 435 } |
455 | |
456 /** | |
457 * Array containing public keys of websites that this filter should apply to | |
458 * @type {?string[]} | |
459 */ | |
460 sitekeys: null, | |
461 | 436 |
462 /** | 437 /** |
463 * Checks whether this filter is active on a domain. | 438 * Checks whether this filter is active on a domain. |
464 * @param {string} [docDomain] domain name of the document that loads the URL | 439 * @param {string} [docDomain] domain name of the document that loads the URL |
465 * @param {string} [sitekey] public key provided by the document | 440 * @param {string} [sitekey] public key provided by the document |
466 * @return {boolean} true in case of the filter being active | 441 * @return {boolean} true in case of the filter being active |
467 */ | 442 */ |
468 isActiveOnDomain(docDomain, sitekey) | 443 isActiveOnDomain(docDomain, sitekey) |
469 { | 444 { |
470 // Sitekeys are case-sensitive so we shouldn't convert them to | 445 // Sitekeys are case-sensitive so we shouldn't convert them to |
(...skipping 21 matching lines...) Expand all Loading... | |
492 let isDomainIncluded = this.domains.get(docDomain); | 467 let isDomainIncluded = this.domains.get(docDomain); |
493 if (typeof isDomainIncluded != "undefined") | 468 if (typeof isDomainIncluded != "undefined") |
494 return isDomainIncluded; | 469 return isDomainIncluded; |
495 | 470 |
496 let nextDot = docDomain.indexOf("."); | 471 let nextDot = docDomain.indexOf("."); |
497 if (nextDot < 0) | 472 if (nextDot < 0) |
498 break; | 473 break; |
499 docDomain = docDomain.substr(nextDot + 1); | 474 docDomain = docDomain.substr(nextDot + 1); |
500 } | 475 } |
501 return this.domains.get(""); | 476 return this.domains.get(""); |
502 }, | 477 } |
503 | 478 |
504 /** | 479 /** |
505 * Checks whether this filter is active only on a domain and its subdomains. | 480 * Checks whether this filter is active only on a domain and its subdomains. |
506 * @param {string} docDomain | 481 * @param {string} docDomain |
507 * @return {boolean} | 482 * @return {boolean} |
508 */ | 483 */ |
509 isActiveOnlyOnDomain(docDomain) | 484 isActiveOnlyOnDomain(docDomain) |
510 { | 485 { |
511 if (!docDomain || !this.domains || this.domains.get("")) | 486 if (!docDomain || !this.domains || this.domains.get("")) |
512 return false; | 487 return false; |
513 | 488 |
514 docDomain = docDomain.replace(/\.+$/, "").toUpperCase(); | 489 docDomain = docDomain.replace(/\.+$/, "").toUpperCase(); |
515 | 490 |
516 for (let [domain, isIncluded] of this.domains) | 491 for (let [domain, isIncluded] of this.domains) |
517 { | 492 { |
518 if (isIncluded && domain != docDomain) | 493 if (isIncluded && domain != docDomain) |
519 { | 494 { |
520 if (domain.length <= docDomain.length) | 495 if (domain.length <= docDomain.length) |
521 return false; | 496 return false; |
522 | 497 |
523 if (!domain.endsWith("." + docDomain)) | 498 if (!domain.endsWith("." + docDomain)) |
524 return false; | 499 return false; |
525 } | 500 } |
526 } | 501 } |
527 | 502 |
528 return true; | 503 return true; |
529 }, | 504 } |
530 | 505 |
531 /** | 506 /** |
532 * Checks whether this filter is generic or specific | 507 * Checks whether this filter is generic or specific |
533 * @return {boolean} | 508 * @return {boolean} |
534 */ | 509 */ |
535 isGeneric() | 510 isGeneric() |
536 { | 511 { |
537 return !(this.sitekeys && this.sitekeys.length) && | 512 return !(this.sitekeys && this.sitekeys.length) && |
538 (!this.domains || this.domains.get("")); | 513 (!this.domains || this.domains.get("")); |
539 }, | 514 } |
540 | 515 |
541 /** | 516 /** |
542 * See Filter.serialize() | 517 * See Filter.serialize() |
543 * @inheritdoc | 518 * @inheritdoc |
544 */ | 519 */ |
545 serialize(buffer) | 520 serialize(buffer) |
546 { | 521 { |
547 if (this._disabled || this._hitCount || this._lastHit) | 522 if (this._disabled || this._hitCount || this._lastHit) |
548 { | 523 { |
549 Filter.prototype.serialize.call(this, buffer); | 524 Filter.prototype.serialize.call(this, buffer); |
550 if (this._disabled) | 525 if (this._disabled) |
551 buffer.push("disabled=true"); | 526 buffer.push("disabled=true"); |
552 if (this._hitCount) | 527 if (this._hitCount) |
553 buffer.push("hitCount=" + this._hitCount); | 528 buffer.push("hitCount=" + this._hitCount); |
554 if (this._lastHit) | 529 if (this._lastHit) |
555 buffer.push("lastHit=" + this._lastHit); | 530 buffer.push("lastHit=" + this._lastHit); |
556 } | 531 } |
557 } | 532 } |
533 } | |
534 | |
535 exports.ActiveFilter = ActiveFilter; | |
536 | |
537 Object.assign(ActiveFilter.prototype, { | |
538 _disabled: false, | |
539 _hitCount: 0, | |
540 _lastHit: 0 | |
558 }); | 541 }); |
559 | 542 |
560 /** | 543 /** |
544 * String that the domains property should be generated from | |
545 * @type {?string} | |
546 */ | |
547 ActiveFilter.prototype.domainSource = null; | |
Manish Jethani
2018/06/09 13:03:32
Where the instance property is not always assigned
| |
548 | |
549 /** | |
550 * Separator character used in domainSource property, must be | |
551 * overridden by subclasses | |
552 * @type {string} | |
553 */ | |
554 ActiveFilter.prototype.domainSeparator = null; | |
555 | |
556 /** | |
557 * Determines whether domainSource is already upper-case, | |
558 * can be overridden by subclasses. | |
559 * @type {boolean} | |
560 */ | |
561 ActiveFilter.prototype.domainSourceIsUpperCase = false; | |
562 | |
563 /** | |
564 * Array containing public keys of websites that this filter should apply to | |
565 * @type {?string[]} | |
566 */ | |
567 ActiveFilter.prototype.sitekeys = null; | |
568 | |
569 /** | |
561 * Abstract base class for RegExp-based filters | 570 * Abstract base class for RegExp-based filters |
562 * @param {string} text see Filter() | |
563 * @param {string} regexpSource | |
564 * filter part that the regular expression should be build from | |
565 * @param {number} [contentType] | |
566 * Content types the filter applies to, combination of values from | |
567 * RegExpFilter.typeMap | |
568 * @param {boolean} [matchCase] | |
569 * Defines whether the filter should distinguish between lower and upper case | |
570 * letters | |
571 * @param {string} [domains] | |
572 * Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" | |
573 * @param {boolean} [thirdParty] | |
574 * Defines whether the filter should apply to third-party or first-party | |
575 * content only | |
576 * @param {string} [sitekeys] | |
577 * Public keys of websites that this filter should apply to | |
578 * @constructor | |
579 * @augments ActiveFilter | |
580 */ | 571 */ |
581 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, | 572 class RegExpFilter extends ActiveFilter |
582 thirdParty, sitekeys) | |
583 { | 573 { |
584 ActiveFilter.call(this, text, domains, sitekeys); | 574 /** |
575 * @param {string} text see Filter() | |
576 * @param {string} regexpSource | |
577 * filter part that the regular expression should be build from | |
578 * @param {number} [contentType] | |
579 * Content types the filter applies to, combination of values from | |
580 * RegExpFilter.typeMap | |
581 * @param {boolean} [matchCase] | |
582 * Defines whether the filter should distinguish between lower and upper | |
583 * case letters | |
584 * @param {string} [domains] | |
585 * Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" | |
586 * @param {boolean} [thirdParty] | |
587 * Defines whether the filter should apply to third-party or first-party | |
588 * content only | |
589 * @param {string} [sitekeys] | |
590 * Public keys of websites that this filter should apply to | |
591 */ | |
592 constructor(text, regexpSource, contentType, matchCase, domains, thirdParty, | |
593 sitekeys) | |
594 { | |
595 super(text, domains, sitekeys); | |
585 | 596 |
586 if (contentType != null) | 597 if (contentType != null) |
587 this.contentType = contentType; | 598 this.contentType = contentType; |
588 if (matchCase) | 599 if (matchCase) |
589 this.matchCase = matchCase; | 600 this.matchCase = matchCase; |
590 if (thirdParty != null) | 601 if (thirdParty != null) |
591 this.thirdParty = thirdParty; | 602 this.thirdParty = thirdParty; |
592 if (sitekeys != null) | 603 if (sitekeys != null) |
593 this.sitekeySource = sitekeys; | 604 this.sitekeySource = sitekeys; |
594 | 605 |
595 if (regexpSource.length >= 2 && | 606 if (regexpSource.length >= 2 && |
596 regexpSource[0] == "/" && | 607 regexpSource[0] == "/" && |
597 regexpSource[regexpSource.length - 1] == "/") | 608 regexpSource[regexpSource.length - 1] == "/") |
598 { | 609 { |
599 // The filter is a regular expression - convert it immediately to | 610 // The filter is a regular expression - convert it immediately to |
600 // catch syntax errors | 611 // catch syntax errors |
601 let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), | 612 let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), |
602 this.matchCase ? "" : "i"); | 613 this.matchCase ? "" : "i"); |
603 Object.defineProperty(this, "regexp", {value: regexp}); | 614 Object.defineProperty(this, "regexp", {value: regexp}); |
615 } | |
616 else | |
617 { | |
618 // No need to convert this filter to regular expression yet, do it on | |
619 // demand | |
620 this.regexpSource = regexpSource; | |
621 } | |
604 } | 622 } |
605 else | |
606 { | |
607 // No need to convert this filter to regular expression yet, do it on demand | |
608 this.regexpSource = regexpSource; | |
609 } | |
610 } | |
611 exports.RegExpFilter = RegExpFilter; | |
612 | 623 |
613 RegExpFilter.prototype = extend(ActiveFilter, { | |
614 /** | |
615 * @see ActiveFilter.domainSourceIsUpperCase | |
616 */ | |
617 domainSourceIsUpperCase: true, | |
618 | |
619 /** | |
620 * Number of filters contained, will always be 1 (required to | |
621 * optimize Matcher). | |
622 * @type {number} | |
623 */ | |
624 length: 1, | |
625 | |
626 /** | |
627 * @see ActiveFilter.domainSeparator | |
628 */ | |
629 domainSeparator: "|", | |
630 | |
631 /** | |
632 * Expression from which a regular expression should be generated - | |
633 * for delayed creation of the regexp property | |
634 * @type {string} | |
635 */ | |
636 regexpSource: null, | |
637 /** | 624 /** |
638 * Regular expression to be used when testing against this filter | 625 * Regular expression to be used when testing against this filter |
639 * @type {RegExp} | 626 * @type {RegExp} |
640 */ | 627 */ |
641 get regexp() | 628 get regexp() |
642 { | 629 { |
643 let source = Filter.toRegExp(this.regexpSource); | 630 let source = Filter.toRegExp(this.regexpSource); |
644 let regexp = new RegExp(source, this.matchCase ? "" : "i"); | 631 let regexp = new RegExp(source, this.matchCase ? "" : "i"); |
645 Object.defineProperty(this, "regexp", {value: regexp}); | 632 Object.defineProperty(this, "regexp", {value: regexp}); |
646 this.regexpSource = null; | 633 this.regexpSource = null; |
647 return regexp; | 634 return regexp; |
648 }, | 635 } |
649 /** | |
650 * Content types the filter applies to, combination of values from | |
651 * RegExpFilter.typeMap | |
652 * @type {number} | |
653 */ | |
654 contentType: 0x7FFFFFFF, | |
655 /** | |
656 * Defines whether the filter should distinguish between lower and | |
657 * upper case letters | |
658 * @type {boolean} | |
659 */ | |
660 matchCase: false, | |
661 /** | |
662 * Defines whether the filter should apply to third-party or | |
663 * first-party content only. Can be null (apply to all content). | |
664 * @type {?boolean} | |
665 */ | |
666 thirdParty: null, | |
667 | 636 |
668 /** | |
669 * String that the sitekey property should be generated from | |
670 * @type {?string} | |
671 */ | |
672 sitekeySource: null, | |
673 | |
674 /** | |
675 * @see ActiveFilter.sitekeys | |
676 */ | |
677 get sitekeys() | 637 get sitekeys() |
678 { | 638 { |
679 let sitekeys = null; | 639 let sitekeys = null; |
680 | 640 |
681 if (this.sitekeySource) | 641 if (this.sitekeySource) |
682 { | 642 { |
683 sitekeys = this.sitekeySource.split("|"); | 643 sitekeys = this.sitekeySource.split("|"); |
684 this.sitekeySource = null; | 644 this.sitekeySource = null; |
685 } | 645 } |
686 | 646 |
687 Object.defineProperty( | 647 Object.defineProperty( |
688 this, "sitekeys", {value: sitekeys, enumerable: true} | 648 this, "sitekeys", {value: sitekeys, enumerable: true} |
689 ); | 649 ); |
690 return this.sitekeys; | 650 return this.sitekeys; |
691 }, | 651 } |
692 | 652 |
693 /** | 653 /** |
694 * Tests whether the URL matches this filter | 654 * Tests whether the URL matches this filter |
695 * @param {string} location URL to be tested | 655 * @param {string} location URL to be tested |
696 * @param {number} typeMask bitmask of content / request types to match | 656 * @param {number} typeMask bitmask of content / request types to match |
697 * @param {string} [docDomain] domain name of the document that loads the URL | 657 * @param {string} [docDomain] domain name of the document that loads the URL |
698 * @param {boolean} [thirdParty] should be true if the URL is a third-party | 658 * @param {boolean} [thirdParty] should be true if the URL is a third-party |
699 * request | 659 * request |
700 * @param {string} [sitekey] public key provided by the document | 660 * @param {string} [sitekey] public key provided by the document |
701 * @return {boolean} true in case of a match | 661 * @return {boolean} true in case of a match |
702 */ | 662 */ |
703 matches(location, typeMask, docDomain, thirdParty, sitekey) | 663 matches(location, typeMask, docDomain, thirdParty, sitekey) |
704 { | 664 { |
705 if (this.contentType & typeMask && | 665 if (this.contentType & typeMask && |
706 (this.thirdParty == null || this.thirdParty == thirdParty) && | 666 (this.thirdParty == null || this.thirdParty == thirdParty) && |
707 this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location)) | 667 this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location)) |
708 { | 668 { |
709 return true; | 669 return true; |
710 } | 670 } |
711 return false; | 671 return false; |
712 } | 672 } |
713 }); | 673 } |
674 | |
675 exports.RegExpFilter = RegExpFilter; | |
676 | |
677 /** | |
678 * @see ActiveFilter.domainSourceIsUpperCase | |
679 */ | |
680 RegExpFilter.prototype.domainSourceIsUpperCase = true; | |
681 | |
682 /** | |
683 * Number of filters contained, will always be 1 (required to | |
684 * optimize Matcher). | |
685 * @type {number} | |
686 */ | |
687 RegExpFilter.prototype.length = 1; | |
688 | |
689 /** | |
690 * @see ActiveFilter.domainSeparator | |
691 */ | |
692 RegExpFilter.prototype.domainSeparator = "|"; | |
693 | |
694 /** | |
695 * Expression from which a regular expression should be generated - | |
696 * for delayed creation of the regexp property | |
697 * @type {string} | |
698 */ | |
699 RegExpFilter.prototype.regexpSource = null; | |
700 | |
701 /** | |
702 * Content types the filter applies to, combination of values from | |
703 * RegExpFilter.typeMap | |
704 * @type {number} | |
705 */ | |
706 RegExpFilter.prototype.contentType = 0x7FFFFFFF; | |
707 | |
708 /** | |
709 * Defines whether the filter should distinguish between lower and | |
710 * upper case letters | |
711 * @type {boolean} | |
712 */ | |
713 RegExpFilter.prototype.matchCase = false; | |
714 | |
715 /** | |
716 * Defines whether the filter should apply to third-party or | |
717 * first-party content only. Can be null (apply to all content). | |
718 * @type {?boolean} | |
719 */ | |
720 RegExpFilter.prototype.thirdParty = null; | |
721 | |
722 /** | |
723 * String that the sitekey property should be generated from | |
724 * @type {?string} | |
725 */ | |
726 RegExpFilter.prototype.sitekeySource = null; | |
714 | 727 |
715 // Required to optimize Matcher, see also RegExpFilter.prototype.length | 728 // Required to optimize Matcher, see also RegExpFilter.prototype.length |
716 Object.defineProperty(RegExpFilter.prototype, "0", { | 729 Object.defineProperty(RegExpFilter.prototype, "0", { |
717 get() { return this; } | 730 get() { return this; } |
718 }); | 731 }); |
719 | 732 |
720 /** | 733 /** |
721 * Creates a RegExp filter from its text representation | 734 * Creates a RegExp filter from its text representation |
722 * @param {string} text same as in Filter() | 735 * @param {string} text same as in Filter() |
723 * @return {Filter} | 736 * @return {Filter} |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
857 // shouldn't be there by default | 870 // shouldn't be there by default |
858 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.CSP | | 871 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.CSP | |
859 RegExpFilter.typeMap.DOCUMENT | | 872 RegExpFilter.typeMap.DOCUMENT | |
860 RegExpFilter.typeMap.ELEMHIDE | | 873 RegExpFilter.typeMap.ELEMHIDE | |
861 RegExpFilter.typeMap.POPUP | | 874 RegExpFilter.typeMap.POPUP | |
862 RegExpFilter.typeMap.GENERICHIDE | | 875 RegExpFilter.typeMap.GENERICHIDE | |
863 RegExpFilter.typeMap.GENERICBLOCK); | 876 RegExpFilter.typeMap.GENERICBLOCK); |
864 | 877 |
865 /** | 878 /** |
866 * Class for blocking filters | 879 * Class for blocking filters |
867 * @param {string} text see Filter() | |
868 * @param {string} regexpSource see RegExpFilter() | |
869 * @param {number} [contentType] see RegExpFilter() | |
870 * @param {boolean} [matchCase] see RegExpFilter() | |
871 * @param {string} [domains] see RegExpFilter() | |
872 * @param {boolean} [thirdParty] see RegExpFilter() | |
873 * @param {string} [sitekeys] see RegExpFilter() | |
874 * @param {boolean} [collapse] | |
875 * defines whether the filter should collapse blocked content, can be null | |
876 * @param {string} [csp] | |
877 * Content Security Policy to inject when the filter matches | |
878 * @param {?string} [rewrite] | |
879 * The (optional) rule specifying how to rewrite the URL. See | |
880 * BlockingFilter.prototype.rewrite. | |
881 * @constructor | |
882 * @augments RegExpFilter | |
883 */ | 880 */ |
884 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, | 881 class BlockingFilter extends RegExpFilter |
885 thirdParty, sitekeys, collapse, csp, rewrite) | |
886 { | 882 { |
887 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, | 883 /** |
888 thirdParty, sitekeys); | 884 * @param {string} text see Filter() |
885 * @param {string} regexpSource see RegExpFilter() | |
886 * @param {number} [contentType] see RegExpFilter() | |
887 * @param {boolean} [matchCase] see RegExpFilter() | |
888 * @param {string} [domains] see RegExpFilter() | |
889 * @param {boolean} [thirdParty] see RegExpFilter() | |
890 * @param {string} [sitekeys] see RegExpFilter() | |
891 * @param {boolean} [collapse] | |
892 * defines whether the filter should collapse blocked content, can be null | |
893 * @param {string} [csp] | |
894 * Content Security Policy to inject when the filter matches | |
895 * @param {?string} [rewrite] | |
896 * The (optional) rule specifying how to rewrite the URL. See | |
897 * BlockingFilter.prototype.rewrite. | |
898 */ | |
899 constructor(text, regexpSource, contentType, matchCase, domains, thirdParty, | |
900 sitekeys, collapse, csp, rewrite) | |
901 { | |
902 super(text, regexpSource, contentType, matchCase, domains, thirdParty, | |
903 sitekeys); | |
889 | 904 |
890 if (collapse != null) | 905 if (collapse != null) |
891 this.collapse = collapse; | 906 this.collapse = collapse; |
892 | 907 |
893 if (csp != null) | 908 if (csp != null) |
894 this.csp = csp; | 909 this.csp = csp; |
895 | 910 |
896 if (rewrite != null) | 911 if (rewrite != null) |
897 this.rewrite = rewrite; | 912 this.rewrite = rewrite; |
898 } | 913 } |
899 exports.BlockingFilter = BlockingFilter; | |
900 | 914 |
901 BlockingFilter.prototype = extend(RegExpFilter, { | 915 get type() |
902 type: "blocking", | 916 { |
903 | 917 return "blocking"; |
904 /** | 918 } |
905 * Defines whether the filter should collapse blocked content. | |
906 * Can be null (use the global preference). | |
907 * @type {?boolean} | |
908 */ | |
909 collapse: null, | |
910 | |
911 /** | |
912 * Content Security Policy to inject for matching requests. | |
913 * @type {?string} | |
914 */ | |
915 csp: null, | |
916 | |
917 /** | |
918 * The rule specifying how to rewrite the URL. | |
919 * The syntax is similar to the one of String.prototype.replace(). | |
920 * @type {?string} | |
921 */ | |
922 rewrite: null, | |
923 | 919 |
924 /** | 920 /** |
925 * Rewrites an URL. | 921 * Rewrites an URL. |
926 * @param {string} url the URL to rewrite | 922 * @param {string} url the URL to rewrite |
927 * @return {string} the rewritten URL, or the original in case of failure | 923 * @return {string} the rewritten URL, or the original in case of failure |
928 */ | 924 */ |
929 rewriteUrl(url) | 925 rewriteUrl(url) |
930 { | 926 { |
931 try | 927 try |
932 { | 928 { |
933 let rewrittenUrl = new URL(url.replace(this.regexp, this.rewrite), url); | 929 let rewrittenUrl = new URL(url.replace(this.regexp, this.rewrite), url); |
934 if (rewrittenUrl.origin == new URL(url).origin) | 930 if (rewrittenUrl.origin == new URL(url).origin) |
935 return rewrittenUrl.href; | 931 return rewrittenUrl.href; |
936 } | 932 } |
937 catch (e) | 933 catch (e) |
938 { | 934 { |
939 } | 935 } |
940 | 936 |
941 return url; | 937 return url; |
942 } | 938 } |
943 }); | 939 } |
940 | |
941 exports.BlockingFilter = BlockingFilter; | |
942 | |
943 /** | |
944 * Defines whether the filter should collapse blocked content. | |
945 * Can be null (use the global preference). | |
946 * @type {?boolean} | |
947 */ | |
948 BlockingFilter.prototype.collapse = null; | |
949 | |
950 /** | |
951 * Content Security Policy to inject for matching requests. | |
952 * @type {?string} | |
953 */ | |
954 BlockingFilter.prototype.csp = null; | |
955 | |
956 /** | |
957 * The rule specifying how to rewrite the URL. | |
958 * The syntax is similar to the one of String.prototype.replace(). | |
959 * @type {?string} | |
960 */ | |
961 BlockingFilter.prototype.rewrite = null; | |
944 | 962 |
945 /** | 963 /** |
946 * Class for whitelist filters | 964 * Class for whitelist filters |
947 * @param {string} text see Filter() | |
948 * @param {string} regexpSource see RegExpFilter() | |
949 * @param {number} [contentType] see RegExpFilter() | |
950 * @param {boolean} [matchCase] see RegExpFilter() | |
951 * @param {string} [domains] see RegExpFilter() | |
952 * @param {boolean} [thirdParty] see RegExpFilter() | |
953 * @param {string} [sitekeys] see RegExpFilter() | |
954 * @constructor | |
955 * @augments RegExpFilter | |
956 */ | 965 */ |
957 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, | 966 class WhitelistFilter extends RegExpFilter |
958 thirdParty, sitekeys) | |
959 { | 967 { |
960 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, | 968 /** |
961 thirdParty, sitekeys); | 969 * @param {string} text see Filter() |
970 * @param {string} regexpSource see RegExpFilter() | |
971 * @param {number} [contentType] see RegExpFilter() | |
972 * @param {boolean} [matchCase] see RegExpFilter() | |
973 * @param {string} [domains] see RegExpFilter() | |
974 * @param {boolean} [thirdParty] see RegExpFilter() | |
975 * @param {string} [sitekeys] see RegExpFilter() | |
976 */ | |
977 constructor(text, regexpSource, contentType, matchCase, domains, thirdParty, | |
978 sitekeys) | |
979 { | |
980 super(text, regexpSource, contentType, matchCase, domains, thirdParty, | |
981 sitekeys); | |
982 } | |
983 | |
984 get type() | |
985 { | |
986 return "whitelist"; | |
987 } | |
962 } | 988 } |
989 | |
963 exports.WhitelistFilter = WhitelistFilter; | 990 exports.WhitelistFilter = WhitelistFilter; |
964 | 991 |
965 WhitelistFilter.prototype = extend(RegExpFilter, { | |
966 type: "whitelist" | |
967 }); | |
968 | |
969 /** | 992 /** |
970 * Base class for element hiding filters | 993 * Base class for element hiding filters |
971 * @param {string} text see Filter() | |
972 * @param {string} [domains] Host names or domains the filter should be | |
973 * restricted to | |
974 * @param {string} selector CSS selector for the HTML elements that should be | |
975 * hidden | |
976 * @constructor | |
977 * @augments ActiveFilter | |
978 */ | 994 */ |
979 function ElemHideBase(text, domains, selector) | 995 class ElemHideBase extends ActiveFilter |
980 { | 996 { |
981 ActiveFilter.call(this, text, domains || null); | 997 /** |
998 * @param {string} text see Filter() | |
999 * @param {string} [domains] Host names or domains the filter should be | |
1000 * restricted to | |
1001 * @param {string} selector CSS selector for the HTML elements that should | |
1002 * be hidden | |
1003 */ | |
1004 constructor(text, domains, selector) | |
1005 { | |
1006 super(text, domains || null); | |
982 | 1007 |
983 // Braces are being escaped to prevent CSS rule injection. | 1008 /** |
984 this.selector = selector.replace("{", "\\7B ").replace("}", "\\7D "); | 1009 * CSS selector for the HTML elements that should be hidden |
1010 * @type {string} | |
1011 */ | |
1012 // Braces are being escaped to prevent CSS rule injection. | |
1013 this.selector = selector.replace("{", "\\7B ").replace("}", "\\7D "); | |
1014 } | |
985 } | 1015 } |
1016 | |
986 exports.ElemHideBase = ElemHideBase; | 1017 exports.ElemHideBase = ElemHideBase; |
987 | 1018 |
988 ElemHideBase.prototype = extend(ActiveFilter, { | 1019 /** |
989 /** | 1020 * @see ActiveFilter.domainSeparator |
990 * @see ActiveFilter.domainSeparator | 1021 */ |
991 */ | 1022 ElemHideBase.prototype.domainSeparator = ","; |
992 domainSeparator: ",", | |
993 | |
994 /** | |
995 * CSS selector for the HTML elements that should be hidden | |
996 * @type {string} | |
997 */ | |
998 selector: null | |
999 }); | |
1000 | 1023 |
1001 /** | 1024 /** |
1002 * Creates an element hiding filter from a pre-parsed text representation | 1025 * Creates an element hiding filter from a pre-parsed text representation |
1003 * | 1026 * |
1004 * @param {string} text same as in Filter() | 1027 * @param {string} text same as in Filter() |
1005 * @param {string} [domains] | 1028 * @param {string} [domains] |
1006 * domains part of the text representation | 1029 * domains part of the text representation |
1007 * @param {string} [type] | 1030 * @param {string} [type] |
1008 * rule type, either empty or @ (exception) or ? (emulation rule) | 1031 * rule type, either empty or @ (exception) or ? (emulation rule) |
1009 * @param {string} selector raw CSS selector | 1032 * @param {string} selector raw CSS selector |
(...skipping 19 matching lines...) Expand all Loading... | |
1029 return new InvalidFilter(text, "filter_elemhideemulation_nodomain"); | 1052 return new InvalidFilter(text, "filter_elemhideemulation_nodomain"); |
1030 | 1053 |
1031 return new ElemHideEmulationFilter(text, domains, selector); | 1054 return new ElemHideEmulationFilter(text, domains, selector); |
1032 } | 1055 } |
1033 | 1056 |
1034 return new ElemHideFilter(text, domains, selector); | 1057 return new ElemHideFilter(text, domains, selector); |
1035 }; | 1058 }; |
1036 | 1059 |
1037 /** | 1060 /** |
1038 * Class for element hiding filters | 1061 * Class for element hiding filters |
1039 * @param {string} text see Filter() | |
1040 * @param {string} [domains] see ElemHideBase() | |
1041 * @param {string} selector see ElemHideBase() | |
1042 * @constructor | |
1043 * @augments ElemHideBase | |
1044 */ | 1062 */ |
1045 function ElemHideFilter(text, domains, selector) | 1063 class ElemHideFilter extends ElemHideBase |
1046 { | 1064 { |
1047 ElemHideBase.call(this, text, domains, selector); | 1065 /** |
1066 * @param {string} text see Filter() | |
1067 * @param {string} [domains] see ElemHideBase() | |
1068 * @param {string} selector see ElemHideBase() | |
1069 */ | |
1070 constructor(text, domains, selector) | |
1071 { | |
1072 super(text, domains, selector); | |
1073 } | |
1074 | |
1075 get type() | |
1076 { | |
1077 return "elemhide"; | |
1078 } | |
1048 } | 1079 } |
1080 | |
1049 exports.ElemHideFilter = ElemHideFilter; | 1081 exports.ElemHideFilter = ElemHideFilter; |
1050 | 1082 |
1051 ElemHideFilter.prototype = extend(ElemHideBase, { | |
1052 type: "elemhide" | |
1053 }); | |
1054 | |
1055 /** | 1083 /** |
1056 * Class for element hiding exceptions | 1084 * Class for element hiding exceptions |
1057 * @param {string} text see Filter() | |
1058 * @param {string} [domains] see ElemHideBase() | |
1059 * @param {string} selector see ElemHideBase() | |
1060 * @constructor | |
1061 * @augments ElemHideBase | |
1062 */ | 1085 */ |
1063 function ElemHideException(text, domains, selector) | 1086 class ElemHideException extends ElemHideBase |
1064 { | 1087 { |
1065 ElemHideBase.call(this, text, domains, selector); | 1088 /** |
1089 * @param {string} text see Filter() | |
1090 * @param {string} [domains] see ElemHideBase() | |
1091 * @param {string} selector see ElemHideBase() | |
1092 */ | |
1093 constructor(text, domains, selector) | |
1094 { | |
1095 super(text, domains, selector); | |
1096 } | |
1097 | |
1098 get type() | |
1099 { | |
1100 return "elemhideexception"; | |
1101 } | |
1066 } | 1102 } |
1103 | |
1067 exports.ElemHideException = ElemHideException; | 1104 exports.ElemHideException = ElemHideException; |
1068 | 1105 |
1069 ElemHideException.prototype = extend(ElemHideBase, { | |
1070 type: "elemhideexception" | |
1071 }); | |
1072 | |
1073 /** | 1106 /** |
1074 * Class for element hiding emulation filters | 1107 * Class for element hiding emulation filters |
1075 * @param {string} text see Filter() | |
1076 * @param {string} domains see ElemHideBase() | |
1077 * @param {string} selector see ElemHideBase() | |
1078 * @constructor | |
1079 * @augments ElemHideBase | |
1080 */ | 1108 */ |
1081 function ElemHideEmulationFilter(text, domains, selector) | 1109 class ElemHideEmulationFilter extends ElemHideBase |
1082 { | 1110 { |
1083 ElemHideBase.call(this, text, domains, selector); | 1111 /** |
1112 * @param {string} text see Filter() | |
1113 * @param {string} domains see ElemHideBase() | |
1114 * @param {string} selector see ElemHideBase() | |
1115 */ | |
1116 constructor(text, domains, selector) | |
1117 { | |
1118 super(text, domains, selector); | |
1119 } | |
1120 | |
1121 get type() | |
1122 { | |
1123 return "elemhideemulation"; | |
1124 } | |
1084 } | 1125 } |
1126 | |
1085 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; | 1127 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; |
1086 | |
1087 ElemHideEmulationFilter.prototype = extend(ElemHideBase, { | |
1088 type: "elemhideemulation" | |
1089 }); | |
OLD | NEW |