Left: | ||
Right: |
OLD | NEW |
---|---|
1 /* | 1 /* |
2 * This file is part of Adblock Plus <http://adblockplus.org/>, | 2 * This file is part of Adblock Plus <http://adblockplus.org/>, |
3 * Copyright (C) 2006-2014 Eyeo GmbH | 3 * Copyright (C) 2006-2014 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 /** | 18 /** |
19 * @fileOverview Definition of Filter class and its subclasses. | 19 * @fileOverview Definition of Filter class and its subclasses. |
20 */ | 20 */ |
21 | 21 |
22 let {FilterNotifier} = require("filterNotifier"); | 22 let {FilterNotifier} = require("filterNotifier"); |
23 let {Utils} = require("utils"); | |
Wladimir Palant
2014/08/01 10:50:21
This is a pretty self-contained module, it shouldn
Thomas Greiner
2014/08/11 17:18:15
Done.
| |
23 | 24 |
24 /** | 25 /** |
25 * Abstract base class for filters | 26 * Abstract base class for filters |
26 * | 27 * |
27 * @param {String} text string representation of the filter | 28 * @param {String} text string representation of the filter |
28 * @constructor | 29 * @constructor |
29 */ | 30 */ |
30 function Filter(text) | 31 function Filter(text) |
31 { | 32 { |
32 this.text = text; | 33 this.text = text; |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 /** | 210 /** |
210 * See Filter.serialize() | 211 * See Filter.serialize() |
211 */ | 212 */ |
212 serialize: function(buffer) {} | 213 serialize: function(buffer) {} |
213 }; | 214 }; |
214 | 215 |
215 /** | 216 /** |
216 * Abstract base class for filters that can get hits | 217 * Abstract base class for filters that can get hits |
217 * @param {String} text see Filter() | 218 * @param {String} text see Filter() |
218 * @param {String} domains (optional) Domains that the filter is restricted to separated by domainSeparator e.g. "foo.com|bar.com|~baz.com" | 219 * @param {String} domains (optional) Domains that the filter is restricted to separated by domainSeparator e.g. "foo.com|bar.com|~baz.com" |
220 * @param {String} siteKeys (optional) Public keys of websites that this filter should apply to, e.g. "foo|bar|~baz" | |
Wladimir Palant
2014/08/01 10:50:21
I don't really see how exceptions are useful here,
Thomas Greiner
2014/08/11 17:18:15
The issue stated that $sitekey should be handled e
| |
219 * @constructor | 221 * @constructor |
220 * @augments Filter | 222 * @augments Filter |
221 */ | 223 */ |
222 function ActiveFilter(text, domains) | 224 function ActiveFilter(text, domains, siteKeys) |
223 { | 225 { |
224 Filter.call(this, text); | 226 Filter.call(this, text); |
225 | 227 |
226 this.domainSource = domains; | 228 this.domainSource = domains; |
229 this.siteKeySource = siteKeys; | |
Thomas Greiner
2014/08/11 17:18:15
Done.
| |
227 } | 230 } |
228 exports.ActiveFilter = ActiveFilter; | 231 exports.ActiveFilter = ActiveFilter; |
229 | 232 |
230 ActiveFilter.prototype = | 233 ActiveFilter.prototype = |
231 { | 234 { |
232 __proto__: Filter.prototype, | 235 __proto__: Filter.prototype, |
233 | 236 |
234 _disabled: false, | 237 _disabled: false, |
235 _hitCount: 0, | 238 _hitCount: 0, |
236 _lastHit: 0, | 239 _lastHit: 0, |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
374 } | 377 } |
375 | 378 |
376 this.domainSource = null; | 379 this.domainSource = null; |
377 } | 380 } |
378 | 381 |
379 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); | 382 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); |
380 return this.domains; | 383 return this.domains; |
381 }, | 384 }, |
382 | 385 |
383 /** | 386 /** |
387 * String that the siteKey property should be generated from | |
388 * @type String | |
389 */ | |
390 siteKeySource: null, | |
391 | |
392 /** | |
393 * Map containing public keys of websites that this filter should apply to | |
394 * @type Object | |
395 */ | |
396 get siteKeys() | |
397 { | |
398 let siteKeys = null; | |
399 | |
400 if (this.siteKeySource) | |
401 { | |
402 let source = this.siteKeySource; | |
403 let list = source.split("|"); | |
404 if (list.length == 1 && list[0][0] != "~") | |
405 { | |
406 // Fast track for the common one-sitekey scenario | |
407 siteKeys = {__proto__: null, "": false}; | |
408 siteKeys[list[0]] = true; | |
409 } | |
410 else | |
411 { | |
412 let hasIncludes = false; | |
413 for (let i = 0; i < list.length; i++) | |
414 { | |
415 let siteKey = list[i]; | |
416 if (siteKey == "") | |
417 continue; | |
418 | |
419 let include; | |
420 if (siteKey[0] == "~") | |
421 { | |
422 include = false; | |
423 siteKey = siteKey.substr(1); | |
424 } | |
425 else | |
426 { | |
427 include = true; | |
428 hasIncludes = true; | |
429 } | |
430 | |
431 if (!siteKeys) | |
432 siteKeys = Object.create(null); | |
433 | |
434 siteKeys[siteKey] = include; | |
435 } | |
436 siteKeys[""] = !hasIncludes; | |
437 } | |
438 | |
439 this.siteKeySource = null; | |
440 } | |
441 | |
442 Object.defineProperty(this, "siteKeys", {value: siteKeys, enumerable: true}) ; | |
443 return this.siteKeys; | |
444 }, | |
445 | |
446 /** | |
384 * Checks whether this filter is active on a domain. | 447 * Checks whether this filter is active on a domain. |
385 */ | 448 */ |
386 isActiveOnDomain: function(/**String*/ docDomain) /**Boolean*/ | 449 isActiveOnDomain: function(/**String*/ docDomain) /**Boolean*/ |
387 { | 450 { |
388 // If no domains are set the rule matches everywhere | 451 // If no domains are set the rule matches everywhere |
389 if (!this.domains) | 452 if (!this.domains) |
390 return true; | 453 return true; |
391 | 454 |
392 // If the document has no host name, match only if the filter isn't restrict ed to specific domains | 455 // If the document has no host name, match only if the filter isn't restrict ed to specific domains |
393 if (!docDomain) | 456 if (!docDomain) |
(...skipping 29 matching lines...) Expand all Loading... | |
423 docDomain = docDomain.toUpperCase(); | 486 docDomain = docDomain.toUpperCase(); |
424 | 487 |
425 for (let domain in this.domains) | 488 for (let domain in this.domains) |
426 if (this.domains[domain] && domain != docDomain && (domain.length <= docDo main.length || domain.indexOf("." + docDomain) != domain.length - docDomain.leng th - 1)) | 489 if (this.domains[domain] && domain != docDomain && (domain.length <= docDo main.length || domain.indexOf("." + docDomain) != domain.length - docDomain.leng th - 1)) |
427 return false; | 490 return false; |
428 | 491 |
429 return true; | 492 return true; |
430 }, | 493 }, |
431 | 494 |
432 /** | 495 /** |
496 * Checks whether the provided site key is valid and active on a page | |
497 */ | |
498 isActiveOnPage: function(/**String*/ docDomain, /**String*/ docLocation, /**St ring*/ siteKey) /**Boolean*/ | |
Wladimir Palant
2014/08/01 10:50:21
I don't think that this is the right place to perf
Thomas Greiner
2014/08/11 17:18:15
Done.
| |
499 { | |
500 // If no site keys are set the rule matches everywhere | |
501 if (!this.siteKeys) | |
502 return true; | |
503 | |
504 // If the document has not provided a sitekey, match only if the filter isn' t restricted by sitekeys | |
505 if (!docDomain || !siteKey) | |
506 return this.siteKeys[""]; | |
507 | |
508 // If the document has provided an invalid sitekey the filter should not mat ch | |
509 if (siteKey.indexOf("_") < 0) | |
510 return false; | |
511 | |
512 let [key, signature] = siteKey.split("_", 2); | |
513 let formattedKey = key.replace(/=/g, "").toUpperCase(); | |
514 if (!(formattedKey in this.siteKeys) || !Utils.crypto) | |
515 return this.siteKeys[""]; | |
516 | |
517 // Website specifies a key that we know but is the signature valid? | |
518 let uri = Services.io.newURI(docLocation, null, null); | |
519 let host = uri.asciiHost; | |
520 if (uri.port > 0) | |
521 host += ":" + uri.port; | |
522 let params = [ | |
523 uri.path.replace(/#.*/, ""), // REQUEST_URI | |
524 host, // HTTP_HOST | |
525 Utils.httpProtocol.userAgent // HTTP_USER_AGENT | |
526 ]; | |
527 if (Utils.verifySignature(key, signature, params.join("\0"))) | |
528 return this.siteKeys[formattedKey]; | |
529 | |
530 return !this.siteKeys[formattedKey]; | |
531 }, | |
532 | |
533 /** | |
433 * See Filter.serialize() | 534 * See Filter.serialize() |
434 */ | 535 */ |
435 serialize: function(buffer) | 536 serialize: function(buffer) |
436 { | 537 { |
437 if (this._disabled || this._hitCount || this._lastHit) | 538 if (this._disabled || this._hitCount || this._lastHit) |
438 { | 539 { |
439 Filter.prototype.serialize.call(this, buffer); | 540 Filter.prototype.serialize.call(this, buffer); |
440 if (this._disabled) | 541 if (this._disabled) |
441 buffer.push("disabled=true"); | 542 buffer.push("disabled=true"); |
442 if (this._hitCount) | 543 if (this._hitCount) |
443 buffer.push("hitCount=" + this._hitCount); | 544 buffer.push("hitCount=" + this._hitCount); |
444 if (this._lastHit) | 545 if (this._lastHit) |
445 buffer.push("lastHit=" + this._lastHit); | 546 buffer.push("lastHit=" + this._lastHit); |
446 } | 547 } |
447 } | 548 } |
448 }; | 549 }; |
449 | 550 |
450 /** | 551 /** |
451 * Abstract base class for RegExp-based filters | 552 * Abstract base class for RegExp-based filters |
452 * @param {String} text see Filter() | 553 * @param {String} text see Filter() |
453 * @param {String} regexpSource filter part that the regular expression should b e build from | 554 * @param {String} regexpSource filter part that the regular expression should b e build from |
454 * @param {Number} contentType (optional) Content types the filter applies to, combination of values from RegExpFilter.typeMap | 555 * @param {Number} contentType (optional) Content types the filter applies to, combination of values from RegExpFilter.typeMap |
455 * @param {Boolean} matchCase (optional) Defines whether the filter should dis tinguish between lower and upper case letters | 556 * @param {Boolean} matchCase (optional) Defines whether the filter should dis tinguish between lower and upper case letters |
456 * @param {String} domains (optional) Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" | 557 * @param {String} domains (optional) Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" |
457 * @param {Boolean} thirdParty (optional) Defines whether the filter should app ly to third-party or first-party content only | 558 * @param {Boolean} thirdParty (optional) Defines whether the filter should app ly to third-party or first-party content only |
559 * @param {String} siteKeys (optional) Public keys of websites that this fil ter should apply to, e.g. "foo|bar|~baz" | |
458 * @constructor | 560 * @constructor |
459 * @augments ActiveFilter | 561 * @augments ActiveFilter |
460 */ | 562 */ |
461 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, third Party) | 563 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, third Party, siteKeys) |
462 { | 564 { |
463 ActiveFilter.call(this, text, domains); | 565 ActiveFilter.call(this, text, domains, siteKeys); |
464 | 566 |
465 if (contentType != null) | 567 if (contentType != null) |
466 this.contentType = contentType; | 568 this.contentType = contentType; |
467 if (matchCase) | 569 if (matchCase) |
468 this.matchCase = matchCase; | 570 this.matchCase = matchCase; |
469 if (thirdParty != null) | 571 if (thirdParty != null) |
470 this.thirdParty = thirdParty; | 572 this.thirdParty = thirdParty; |
471 | 573 |
472 if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpS ource.length - 1] == "/") | 574 if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpS ource.length - 1] == "/") |
473 { | 575 { |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
547 * @type Boolean | 649 * @type Boolean |
548 */ | 650 */ |
549 thirdParty: null, | 651 thirdParty: null, |
550 | 652 |
551 /** | 653 /** |
552 * Tests whether the URL matches this filter | 654 * Tests whether the URL matches this filter |
553 * @param {String} location URL to be tested | 655 * @param {String} location URL to be tested |
554 * @param {String} contentType content type identifier of the URL | 656 * @param {String} contentType content type identifier of the URL |
555 * @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 |
556 * @param {Boolean} thirdParty should be true if the URL is a third-party requ est | 658 * @param {Boolean} thirdParty should be true if the URL is a third-party requ est |
659 * @param {String} docLocation location of the document that loads the URL | |
660 * @param {String} siteKey public key provided by the document | |
557 * @return {Boolean} true in case of a match | 661 * @return {Boolean} true in case of a match |
558 */ | 662 */ |
559 matches: function(location, contentType, docDomain, thirdParty) | 663 matches: function(location, contentType, docDomain, thirdParty, docLocation, s iteKey) |
560 { | 664 { |
561 if (this.regexp.test(location) && | 665 if (this.regexp.test(location) && |
562 (RegExpFilter.typeMap[contentType] & this.contentType) != 0 && | 666 (RegExpFilter.typeMap[contentType] & this.contentType) != 0 && |
563 (this.thirdParty == null || this.thirdParty == thirdParty) && | 667 (this.thirdParty == null || this.thirdParty == thirdParty) && |
564 this.isActiveOnDomain(docDomain)) | 668 this.isActiveOnDomain(docDomain) && |
669 this.isActiveOnPage(docDomain, docLocation, siteKey)) | |
565 { | 670 { |
566 return true; | 671 return true; |
567 } | 672 } |
568 | 673 |
569 return false; | 674 return false; |
570 } | 675 } |
571 }; | 676 }; |
572 | 677 |
573 // Required to optimize Matcher, see also RegExpFilter.prototype.length | 678 // Required to optimize Matcher, see also RegExpFilter.prototype.length |
574 Object.defineProperty(RegExpFilter.prototype, "0", | 679 Object.defineProperty(RegExpFilter.prototype, "0", |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
632 domains = value; | 737 domains = value; |
633 else if (option == "THIRD_PARTY") | 738 else if (option == "THIRD_PARTY") |
634 thirdParty = true; | 739 thirdParty = true; |
635 else if (option == "~THIRD_PARTY") | 740 else if (option == "~THIRD_PARTY") |
636 thirdParty = false; | 741 thirdParty = false; |
637 else if (option == "COLLAPSE") | 742 else if (option == "COLLAPSE") |
638 collapse = true; | 743 collapse = true; |
639 else if (option == "~COLLAPSE") | 744 else if (option == "~COLLAPSE") |
640 collapse = false; | 745 collapse = false; |
641 else if (option == "SITEKEY" && typeof value != "undefined") | 746 else if (option == "SITEKEY" && typeof value != "undefined") |
642 siteKeys = value.split(/\|/); | 747 siteKeys = value; |
643 else | 748 else |
644 return new InvalidFilter(origText, "Unknown option " + option.toLowerCas e()); | 749 return new InvalidFilter(origText, "Unknown option " + option.toLowerCas e()); |
645 } | 750 } |
646 } | 751 } |
647 | 752 |
648 if (!blocking && (contentType == null || (contentType & RegExpFilter.typeMap.D OCUMENT)) && | 753 if (!blocking && (contentType == null || (contentType & RegExpFilter.typeMap.D OCUMENT)) && |
649 (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text )) | 754 (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text )) |
650 { | 755 { |
651 // Exception filters shouldn't apply to pages by default unless they start w ith a protocol name | 756 // Exception filters shouldn't apply to pages by default unless they start w ith a protocol name |
652 if (contentType == null) | 757 if (contentType == null) |
653 contentType = RegExpFilter.prototype.contentType; | 758 contentType = RegExpFilter.prototype.contentType; |
654 contentType &= ~RegExpFilter.typeMap.DOCUMENT; | 759 contentType &= ~RegExpFilter.typeMap.DOCUMENT; |
655 } | 760 } |
656 if (!blocking && siteKeys) | |
657 contentType = RegExpFilter.typeMap.DOCUMENT; | |
658 | 761 |
659 try | 762 try |
660 { | 763 { |
661 if (blocking) | 764 if (blocking) |
662 return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, collapse); | 765 return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, siteKeys, collapse); |
663 else | 766 else |
664 return new WhitelistFilter(origText, text, contentType, matchCase, domains , thirdParty, siteKeys); | 767 return new WhitelistFilter(origText, text, contentType, matchCase, domains , thirdParty, siteKeys); |
665 } | 768 } |
666 catch (e) | 769 catch (e) |
667 { | 770 { |
668 return new InvalidFilter(origText, e); | 771 return new InvalidFilter(origText, e); |
669 } | 772 } |
670 } | 773 } |
671 | 774 |
672 /** | 775 /** |
(...skipping 25 matching lines...) Expand all Loading... | |
698 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | RegExpFi lter.typeMap.POPUP); | 801 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | RegExpFi lter.typeMap.POPUP); |
699 | 802 |
700 /** | 803 /** |
701 * Class for blocking filters | 804 * Class for blocking filters |
702 * @param {String} text see Filter() | 805 * @param {String} text see Filter() |
703 * @param {String} regexpSource see RegExpFilter() | 806 * @param {String} regexpSource see RegExpFilter() |
704 * @param {Number} contentType see RegExpFilter() | 807 * @param {Number} contentType see RegExpFilter() |
705 * @param {Boolean} matchCase see RegExpFilter() | 808 * @param {Boolean} matchCase see RegExpFilter() |
706 * @param {String} domains see RegExpFilter() | 809 * @param {String} domains see RegExpFilter() |
707 * @param {Boolean} thirdParty see RegExpFilter() | 810 * @param {Boolean} thirdParty see RegExpFilter() |
811 * @param {String} siteKeys see RegExpFilter() | |
708 * @param {Boolean} collapse defines whether the filter should collapse blocked content, can be null | 812 * @param {Boolean} collapse defines whether the filter should collapse blocked content, can be null |
709 * @constructor | 813 * @constructor |
710 * @augments RegExpFilter | 814 * @augments RegExpFilter |
711 */ | 815 */ |
712 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thi rdParty, collapse) | 816 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thi rdParty, siteKeys, collapse) |
713 { | 817 { |
714 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty); | 818 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty, siteKeys); |
715 | 819 |
716 this.collapse = collapse; | 820 this.collapse = collapse; |
717 } | 821 } |
718 exports.BlockingFilter = BlockingFilter; | 822 exports.BlockingFilter = BlockingFilter; |
719 | 823 |
720 BlockingFilter.prototype = | 824 BlockingFilter.prototype = |
721 { | 825 { |
722 __proto__: RegExpFilter.prototype, | 826 __proto__: RegExpFilter.prototype, |
723 | 827 |
724 /** | 828 /** |
725 * Defines whether the filter should collapse blocked content. Can be null (us e the global preference). | 829 * Defines whether the filter should collapse blocked content. Can be null (us e the global preference). |
726 * @type Boolean | 830 * @type Boolean |
727 */ | 831 */ |
728 collapse: null | 832 collapse: null |
729 }; | 833 }; |
730 | 834 |
731 /** | 835 /** |
732 * Class for whitelist filters | 836 * Class for whitelist filters |
733 * @param {String} text see Filter() | 837 * @param {String} text see Filter() |
734 * @param {String} regexpSource see RegExpFilter() | 838 * @param {String} regexpSource see RegExpFilter() |
735 * @param {Number} contentType see RegExpFilter() | 839 * @param {Number} contentType see RegExpFilter() |
736 * @param {Boolean} matchCase see RegExpFilter() | 840 * @param {Boolean} matchCase see RegExpFilter() |
737 * @param {String} domains see RegExpFilter() | 841 * @param {String} domains see RegExpFilter() |
738 * @param {Boolean} thirdParty see RegExpFilter() | 842 * @param {Boolean} thirdParty see RegExpFilter() |
739 * @param {String[]} siteKeys public keys of websites that this filter should ap ply to | 843 * @param {String} siteKeys see RegExpFilter() |
740 * @constructor | 844 * @constructor |
741 * @augments RegExpFilter | 845 * @augments RegExpFilter |
742 */ | 846 */ |
743 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, th irdParty, siteKeys) | 847 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, th irdParty, siteKeys) |
744 { | 848 { |
745 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty); | 849 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty, siteKeys); |
746 | |
747 if (siteKeys != null) | |
748 this.siteKeys = siteKeys; | |
749 } | 850 } |
750 exports.WhitelistFilter = WhitelistFilter; | 851 exports.WhitelistFilter = WhitelistFilter; |
751 | 852 |
752 WhitelistFilter.prototype = | 853 WhitelistFilter.prototype = |
753 { | 854 { |
754 __proto__: RegExpFilter.prototype, | 855 __proto__: RegExpFilter.prototype |
755 | |
756 /** | |
757 * List of public keys of websites that this filter should apply to | |
758 * @type String[] | |
759 */ | |
760 siteKeys: null | |
761 } | 856 } |
762 | 857 |
763 /** | 858 /** |
764 * Base class for element hiding filters | 859 * Base class for element hiding filters |
765 * @param {String} text see Filter() | 860 * @param {String} text see Filter() |
766 * @param {String} domains (optional) Host names or domains the filter should be restricted to | 861 * @param {String} domains (optional) Host names or domains the filter should be restricted to |
767 * @param {String} selector CSS selector for the HTML elements that should be hidden | 862 * @param {String} selector CSS selector for the HTML elements that should be hidden |
768 * @constructor | 863 * @constructor |
769 * @augments ActiveFilter | 864 * @augments ActiveFilter |
770 */ | 865 */ |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
890 function ElemHideException(text, domains, selector) | 985 function ElemHideException(text, domains, selector) |
891 { | 986 { |
892 ElemHideBase.call(this, text, domains, selector); | 987 ElemHideBase.call(this, text, domains, selector); |
893 } | 988 } |
894 exports.ElemHideException = ElemHideException; | 989 exports.ElemHideException = ElemHideException; |
895 | 990 |
896 ElemHideException.prototype = | 991 ElemHideException.prototype = |
897 { | 992 { |
898 __proto__: ElemHideBase.prototype | 993 __proto__: ElemHideBase.prototype |
899 }; | 994 }; |
OLD | NEW |