LEFT | RIGHT |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-2015 Eyeo GmbH | 3 * Copyright (C) 2006-2015 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"); |
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; |
33 this.subscriptions = []; | 34 this.subscriptions = []; |
34 } | 35 } |
35 exports.Filter = Filter; | 36 exports.Filter = Filter; |
36 | 37 |
37 Filter.prototype = | 38 Filter.prototype = |
38 { | 39 { |
39 /** | 40 /** |
40 * String representation of the filter | 41 * String representation of the filter |
41 * @type String | 42 * @type String |
42 */ | 43 */ |
43 text: null, | 44 text: null, |
44 | 45 |
45 /** | 46 /** |
46 * Filter subscriptions the filter belongs to | 47 * Filter subscriptions the filter belongs to |
47 * @type Array of Subscription | 48 * @type Subscription[] |
48 */ | 49 */ |
49 subscriptions: null, | 50 subscriptions: null, |
50 | 51 |
51 /** | 52 /** |
52 * Serializes the filter to an array of strings for writing out on the disk. | 53 * Serializes the filter to an array of strings for writing out on the disk. |
53 * @param {Array of String} buffer buffer to push the serialization results i
nto | 54 * @param {string[]} buffer buffer to push the serialization results into |
54 */ | 55 */ |
55 serialize: function(buffer) | 56 serialize: function(buffer) |
56 { | 57 { |
57 buffer.push("[Filter]"); | 58 buffer.push("[Filter]"); |
58 buffer.push("text=" + this.text); | 59 buffer.push("text=" + this.text); |
59 }, | 60 }, |
60 | 61 |
61 toString: function() | 62 toString: function() |
62 { | 63 { |
63 return this.text; | 64 return this.text; |
(...skipping 14 matching lines...) Expand all Loading... |
78 /** | 79 /** |
79 * Regular expression that RegExp filters specified as RegExps should match | 80 * Regular expression that RegExp filters specified as RegExps should match |
80 * @type RegExp | 81 * @type RegExp |
81 */ | 82 */ |
82 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[
^,\s]+)?)*)?$/; | 83 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[
^,\s]+)?)*)?$/; |
83 /** | 84 /** |
84 * Regular expression that options on a RegExp filter should match | 85 * Regular expression that options on a RegExp filter should match |
85 * @type RegExp | 86 * @type RegExp |
86 */ | 87 */ |
87 Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/
; | 88 Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/
; |
| 89 /** |
| 90 * Regular expression that CSS property filters should match |
| 91 * Properties must not contain " or ' |
| 92 * @type RegExp |
| 93 */ |
| 94 Filter.csspropertyRegExp = /\[\-abp\-properties=(["'])([^"']+)\1\]/; |
88 | 95 |
89 /** | 96 /** |
90 * Creates a filter of correct type from its text representation - does the basi
c parsing and | 97 * Creates a filter of correct type from its text representation - does the basi
c parsing and |
91 * calls the right constructor then. | 98 * calls the right constructor then. |
92 * | 99 * |
93 * @param {String} text as in Filter() | 100 * @param {String} text as in Filter() |
94 * @return {Filter} | 101 * @return {Filter} |
95 */ | 102 */ |
96 Filter.fromText = function(text) | 103 Filter.fromText = function(text) |
97 { | 104 { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 } | 158 } |
152 else if (Filter.elemhideRegExp.test(text)) | 159 else if (Filter.elemhideRegExp.test(text)) |
153 { | 160 { |
154 // Special treatment for element hiding filters, right side is allowed to co
ntain spaces | 161 // Special treatment for element hiding filters, right side is allowed to co
ntain spaces |
155 let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text); | 162 let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text); |
156 return domain.replace(/\s/g, "") + separator + selector.trim(); | 163 return domain.replace(/\s/g, "") + separator + selector.trim(); |
157 } | 164 } |
158 else | 165 else |
159 return text.replace(/\s/g, ""); | 166 return text.replace(/\s/g, ""); |
160 }; | 167 }; |
| 168 |
| 169 /** |
| 170 * Converts filter text into regular expression string |
| 171 * @param {String} text as in Filter() |
| 172 * @return {String} regular expression representation of filter text |
| 173 */ |
| 174 Filter.toRegExp = function(text) |
| 175 { |
| 176 return text |
| 177 .replace(/\*+/g, "*") // remove multiple wildcards |
| 178 .replace(/\^\|$/, "^") // remove anchors following separator placehold
er |
| 179 .replace(/\W/g, "\\$&") // escape special symbols |
| 180 .replace(/\\\*/g, ".*") // replace wildcards by .* |
| 181 // process separator placeholders (all ANSI characters but alphanumeric char
acters and _%.-) |
| 182 .replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x6
0\\x7B-\\x7F]|$)") |
| 183 .replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?") // process ex
tended anchor at expression start |
| 184 .replace(/^\\\|/, "^") // process anchor at expression start |
| 185 .replace(/\\\|$/, "$") // process anchor at expression end |
| 186 .replace(/^(\.\*)/, "") // remove leading wildcards |
| 187 .replace(/(\.\*)$/, ""); // remove trailing wildcards |
| 188 } |
161 | 189 |
162 /** | 190 /** |
163 * Class for invalid filters | 191 * Class for invalid filters |
164 * @param {String} text see Filter() | 192 * @param {String} text see Filter() |
165 * @param {String} reason Reason why this filter is invalid | 193 * @param {String} reason Reason why this filter is invalid |
166 * @constructor | 194 * @constructor |
167 * @augments Filter | 195 * @augments Filter |
168 */ | 196 */ |
169 function InvalidFilter(text, reason) | 197 function InvalidFilter(text, reason) |
170 { | 198 { |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 | 409 |
382 this.domainSource = null; | 410 this.domainSource = null; |
383 } | 411 } |
384 | 412 |
385 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); | 413 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); |
386 return this.domains; | 414 return this.domains; |
387 }, | 415 }, |
388 | 416 |
389 /** | 417 /** |
390 * 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 |
391 * @type Array of String | 419 * @type string[] |
392 */ | 420 */ |
393 sitekeys: null, | 421 sitekeys: null, |
394 | 422 |
395 /** | 423 /** |
396 * Checks whether this filter is active on a domain. | 424 * Checks whether this filter is active on a domain. |
397 * @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 |
398 * @param {String} [sitekey] public key provided by the document | 426 * @param {String} [sitekey] public key provided by the document |
399 * @return {Boolean} true in case of the filter being active | 427 * @return {Boolean} true in case of the filter being active |
400 */ | 428 */ |
401 isActiveOnDomain: function(docDomain, sitekey) | 429 isActiveOnDomain: function(docDomain, sitekey) |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
440 | 468 |
441 if (this.ignoreTrailingDot) | 469 if (this.ignoreTrailingDot) |
442 docDomain = docDomain.replace(/\.+$/, ""); | 470 docDomain = docDomain.replace(/\.+$/, ""); |
443 docDomain = docDomain.toUpperCase(); | 471 docDomain = docDomain.toUpperCase(); |
444 | 472 |
445 for (let domain in this.domains) | 473 for (let domain in this.domains) |
446 if (this.domains[domain] && domain != docDomain && (domain.length <= docDo
main.length || domain.indexOf("." + docDomain) != domain.length - docDomain.leng
th - 1)) | 474 if (this.domains[domain] && domain != docDomain && (domain.length <= docDo
main.length || domain.indexOf("." + docDomain) != domain.length - docDomain.leng
th - 1)) |
447 return false; | 475 return false; |
448 | 476 |
449 return true; | 477 return true; |
| 478 }, |
| 479 |
| 480 /** |
| 481 * Checks whether this filter is generic or specific |
| 482 */ |
| 483 isGeneric: function() /**Boolean*/ |
| 484 { |
| 485 return !(this.sitekeys && this.sitekeys.length) && |
| 486 (!this.domains || this.domains[""]); |
450 }, | 487 }, |
451 | 488 |
452 /** | 489 /** |
453 * See Filter.serialize() | 490 * See Filter.serialize() |
454 */ | 491 */ |
455 serialize: function(buffer) | 492 serialize: function(buffer) |
456 { | 493 { |
457 if (this._disabled || this._hitCount || this._lastHit) | 494 if (this._disabled || this._hitCount || this._lastHit) |
458 { | 495 { |
459 Filter.prototype.serialize.call(this, buffer); | 496 Filter.prototype.serialize.call(this, buffer); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
536 * @type RegExp | 573 * @type RegExp |
537 */ | 574 */ |
538 get regexp() | 575 get regexp() |
539 { | 576 { |
540 // Despite this property being cached, the getter is called | 577 // Despite this property being cached, the getter is called |
541 // several times on Safari, due to WebKit bug 132872 | 578 // several times on Safari, due to WebKit bug 132872 |
542 let prop = Object.getOwnPropertyDescriptor(this, "regexp"); | 579 let prop = Object.getOwnPropertyDescriptor(this, "regexp"); |
543 if (prop) | 580 if (prop) |
544 return prop.value; | 581 return prop.value; |
545 | 582 |
546 // Remove multiple wildcards | 583 let source = Filter.toRegExp(this.regexpSource); |
547 let source = this.regexpSource | |
548 .replace(/\*+/g, "*") // remove multiple wildcards | |
549 .replace(/\^\|$/, "^") // remove anchors following separator placeho
lder | |
550 .replace(/\W/g, "\\$&") // escape special symbols | |
551 .replace(/\\\*/g, ".*") // replace wildcards by .* | |
552 // process separator placeholders (all ANSI characters but alphanumeric ch
aracters and _%.-) | |
553 .replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\
x60\\x7B-\\x7F]|$)") | |
554 .replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?") // process
extended anchor at expression start | |
555 .replace(/^\\\|/, "^") // process anchor at expression start | |
556 .replace(/\\\|$/, "$") // process anchor at expression end | |
557 .replace(/^(\.\*)/, "") // remove leading wildcards | |
558 .replace(/(\.\*)$/, ""); // remove trailing wildcards | |
559 | |
560 let regexp = new RegExp(source, this.matchCase ? "" : "i"); | 584 let regexp = new RegExp(source, this.matchCase ? "" : "i"); |
561 Object.defineProperty(this, "regexp", {value: regexp}); | 585 Object.defineProperty(this, "regexp", {value: regexp}); |
562 return regexp; | 586 return regexp; |
563 }, | 587 }, |
564 /** | 588 /** |
565 * Content types the filter applies to, combination of values from RegExpFilte
r.typeMap | 589 * Content types the filter applies to, combination of values from RegExpFilte
r.typeMap |
566 * @type Number | 590 * @type Number |
567 */ | 591 */ |
568 contentType: 0x7FFFFFFF, | 592 contentType: 0x7FFFFFFF, |
569 /** | 593 /** |
570 * Defines whether the filter should distinguish between lower and upper case
letters | 594 * Defines whether the filter should distinguish between lower and upper case
letters |
571 * @type Boolean | 595 * @type Boolean |
572 */ | 596 */ |
573 matchCase: false, | 597 matchCase: false, |
574 /** | 598 /** |
575 * Defines whether the filter should apply to third-party or first-party conte
nt only. Can be null (apply to all content). | 599 * Defines whether the filter should apply to third-party or first-party conte
nt only. Can be null (apply to all content). |
576 * @type Boolean | 600 * @type Boolean |
577 */ | 601 */ |
578 thirdParty: null, | 602 thirdParty: null, |
579 | 603 |
580 /** | 604 /** |
581 * String that the sitekey property should be generated from | 605 * String that the sitekey property should be generated from |
582 * @type String | 606 * @type String |
583 */ | 607 */ |
584 sitekeySource: null, | 608 sitekeySource: null, |
585 | 609 |
586 /** | 610 /** |
587 * Array containing public keys of websites that this filter should apply to | 611 * Array containing public keys of websites that this filter should apply to |
588 * @type Array of String | 612 * @type string[] |
589 */ | 613 */ |
590 get sitekeys() | 614 get sitekeys() |
591 { | 615 { |
592 // Despite this property being cached, the getter is called | 616 // Despite this property being cached, the getter is called |
593 // several times on Safari, due to WebKit bug 132872 | 617 // several times on Safari, due to WebKit bug 132872 |
594 let prop = Object.getOwnPropertyDescriptor(this, "sitekeys"); | 618 let prop = Object.getOwnPropertyDescriptor(this, "sitekeys"); |
595 if (prop) | 619 if (prop) |
596 return prop.value; | 620 return prop.value; |
597 | 621 |
598 let sitekeys = null; | 622 let sitekeys = null; |
599 | 623 |
600 if (this.sitekeySource) | 624 if (this.sitekeySource) |
601 { | 625 { |
602 sitekeys = this.sitekeySource.split("|"); | 626 sitekeys = this.sitekeySource.split("|"); |
603 this.sitekeySource = null; | 627 this.sitekeySource = null; |
604 } | 628 } |
605 | 629 |
606 Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true})
; | 630 Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true})
; |
607 return this.sitekeys; | 631 return this.sitekeys; |
608 }, | 632 }, |
609 | 633 |
610 /** | 634 /** |
611 * Tests whether the URL matches this filter | 635 * Tests whether the URL matches this filter |
612 * @param {String} location URL to be tested | 636 * @param {String} location URL to be tested |
613 * @param {String} contentType content type identifier of the URL | 637 * @param {String} typeMask bitmask of content / request types to match |
614 * @param {String} docDomain domain name of the document that loads the URL | 638 * @param {String} docDomain domain name of the document that loads the URL |
615 * @param {Boolean} thirdParty should be true if the URL is a third-party requ
est | 639 * @param {Boolean} thirdParty should be true if the URL is a third-party requ
est |
616 * @param {String} sitekey public key provided by the document | 640 * @param {String} sitekey public key provided by the document |
617 * @return {Boolean} true in case of a match | 641 * @return {Boolean} true in case of a match |
618 */ | 642 */ |
619 matches: function(location, contentType, docDomain, thirdParty, sitekey) | 643 matches: function(location, typeMask, docDomain, thirdParty, sitekey) |
620 { | 644 { |
621 if (this.regexp.test(location) && | 645 if (this.contentType & typeMask && |
622 (RegExpFilter.typeMap[contentType] & this.contentType) != 0 && | |
623 (this.thirdParty == null || this.thirdParty == thirdParty) && | 646 (this.thirdParty == null || this.thirdParty == thirdParty) && |
624 this.isActiveOnDomain(docDomain, sitekey)) | 647 this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location)) |
625 { | 648 { |
626 return true; | 649 return true; |
627 } | 650 } |
628 | 651 |
629 return false; | 652 return false; |
630 } | 653 } |
631 }; | 654 }; |
632 | 655 |
633 // Required to optimize Matcher, see also RegExpFilter.prototype.length | 656 // Required to optimize Matcher, see also RegExpFilter.prototype.length |
634 Object.defineProperty(RegExpFilter.prototype, "0", | 657 Object.defineProperty(RegExpFilter.prototype, "0", |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
698 collapse = true; | 721 collapse = true; |
699 else if (option == "~COLLAPSE") | 722 else if (option == "~COLLAPSE") |
700 collapse = false; | 723 collapse = false; |
701 else if (option == "SITEKEY" && typeof value != "undefined") | 724 else if (option == "SITEKEY" && typeof value != "undefined") |
702 sitekeys = value; | 725 sitekeys = value; |
703 else | 726 else |
704 return new InvalidFilter(origText, "Unknown option " + option.toLowerCas
e()); | 727 return new InvalidFilter(origText, "Unknown option " + option.toLowerCas
e()); |
705 } | 728 } |
706 } | 729 } |
707 | 730 |
708 if (!blocking && (contentType == null || (contentType & RegExpFilter.typeMap.D
OCUMENT)) && | |
709 (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text
)) | |
710 { | |
711 // Exception filters shouldn't apply to pages by default unless they start w
ith a protocol name | |
712 if (contentType == null) | |
713 contentType = RegExpFilter.prototype.contentType; | |
714 contentType &= ~RegExpFilter.typeMap.DOCUMENT; | |
715 } | |
716 | |
717 try | 731 try |
718 { | 732 { |
719 if (blocking) | 733 if (blocking) |
720 return new BlockingFilter(origText, text, contentType, matchCase, domains,
thirdParty, sitekeys, collapse); | 734 return new BlockingFilter(origText, text, contentType, matchCase, domains,
thirdParty, sitekeys, collapse); |
721 else | 735 else |
722 return new WhitelistFilter(origText, text, contentType, matchCase, domains
, thirdParty, sitekeys); | 736 return new WhitelistFilter(origText, text, contentType, matchCase, domains
, thirdParty, sitekeys); |
723 } | 737 } |
724 catch (e) | 738 catch (e) |
725 { | 739 { |
726 return new InvalidFilter(origText, e); | 740 return new InvalidFilter(origText, e); |
(...skipping 20 matching lines...) Expand all Loading... |
747 FONT: 32768, | 761 FONT: 32768, |
748 | 762 |
749 BACKGROUND: 4, // Backwards compat, same as IMAGE | 763 BACKGROUND: 4, // Backwards compat, same as IMAGE |
750 | 764 |
751 POPUP: 0x10000000, | 765 POPUP: 0x10000000, |
752 GENERICBLOCK: 0x20000000, | 766 GENERICBLOCK: 0x20000000, |
753 ELEMHIDE: 0x40000000, | 767 ELEMHIDE: 0x40000000, |
754 GENERICHIDE: 0x80000000 | 768 GENERICHIDE: 0x80000000 |
755 }; | 769 }; |
756 | 770 |
757 // ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't be there by d
efault | 771 // DOCUMENT, ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't |
758 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | | 772 // be there by default |
| 773 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.DOCUMENT | |
| 774 RegExpFilter.typeMap.ELEMHIDE | |
759 RegExpFilter.typeMap.POPUP | | 775 RegExpFilter.typeMap.POPUP | |
760 RegExpFilter.typeMap.GENERICHIDE | | 776 RegExpFilter.typeMap.GENERICHIDE | |
761 RegExpFilter.typeMap.GENERICBLOCK); | 777 RegExpFilter.typeMap.GENERICBLOCK); |
762 | 778 |
763 /** | 779 /** |
764 * Class for blocking filters | 780 * Class for blocking filters |
765 * @param {String} text see Filter() | 781 * @param {String} text see Filter() |
766 * @param {String} regexpSource see RegExpFilter() | 782 * @param {String} regexpSource see RegExpFilter() |
767 * @param {Number} contentType see RegExpFilter() | 783 * @param {Number} contentType see RegExpFilter() |
768 * @param {Boolean} matchCase see RegExpFilter() | 784 * @param {Boolean} matchCase see RegExpFilter() |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
860 }; | 876 }; |
861 | 877 |
862 /** | 878 /** |
863 * Creates an element hiding filter from a pre-parsed text representation | 879 * Creates an element hiding filter from a pre-parsed text representation |
864 * | 880 * |
865 * @param {String} text same as in Filter() | 881 * @param {String} text same as in Filter() |
866 * @param {String} domain domain part of the text representation (can be emp
ty) | 882 * @param {String} domain domain part of the text representation (can be emp
ty) |
867 * @param {String} tagName tag name part (can be empty) | 883 * @param {String} tagName tag name part (can be empty) |
868 * @param {String} attrRules attribute matching rules (can be empty) | 884 * @param {String} attrRules attribute matching rules (can be empty) |
869 * @param {String} selector raw CSS selector (can be empty) | 885 * @param {String} selector raw CSS selector (can be empty) |
870 * @return {ElemHideFilter|ElemHideException|InvalidFilter} | 886 * @return {ElemHideFilter|ElemHideException|CSSPropertyFilter|InvalidFilter} |
871 */ | 887 */ |
872 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules,
selector) | 888 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules,
selector) |
873 { | 889 { |
874 if (!selector) | 890 if (!selector) |
875 { | 891 { |
876 if (tagName == "*") | 892 if (tagName == "*") |
877 tagName = ""; | 893 tagName = ""; |
878 | 894 |
879 let id = null; | 895 let id = null; |
880 let additional = ""; | 896 let additional = ""; |
881 if (attrRules) { | 897 if (attrRules) |
| 898 { |
882 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g); | 899 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g); |
883 for (let rule of attrRules) { | 900 for (let rule of attrRules) |
| 901 { |
884 rule = rule.substr(1, rule.length - 2); | 902 rule = rule.substr(1, rule.length - 2); |
885 let separatorPos = rule.indexOf("="); | 903 let separatorPos = rule.indexOf("="); |
886 if (separatorPos > 0) { | 904 if (separatorPos > 0) |
| 905 { |
887 rule = rule.replace(/=/, '="') + '"'; | 906 rule = rule.replace(/=/, '="') + '"'; |
888 additional += "[" + rule + "]"; | 907 additional += "[" + rule + "]"; |
889 } | 908 } |
890 else { | 909 else |
| 910 { |
891 if (id) | 911 if (id) |
892 { | |
893 let {Utils} = require("utils"); | |
894 return new InvalidFilter(text, Utils.getString("filter_elemhide_dupl
icate_id")); | 912 return new InvalidFilter(text, Utils.getString("filter_elemhide_dupl
icate_id")); |
895 } | 913 |
896 else | 914 id = rule; |
897 id = rule; | |
898 } | 915 } |
899 } | 916 } |
900 } | 917 } |
901 | 918 |
902 if (id) | 919 if (id) |
903 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad
ditional; | 920 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad
ditional; |
904 else if (tagName || additional) | 921 else if (tagName || additional) |
905 selector = tagName + additional; | 922 selector = tagName + additional; |
906 else | 923 else |
907 { | |
908 let {Utils} = require("utils"); | |
909 return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria
")); | 924 return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria
")); |
910 } | 925 } |
911 } | 926 |
912 if (isException) | 927 if (isException) |
913 return new ElemHideException(text, domain, selector); | 928 return new ElemHideException(text, domain, selector); |
914 else | 929 |
915 return new ElemHideFilter(text, domain, selector); | 930 let match = Filter.csspropertyRegExp.exec(selector); |
| 931 if (match) |
| 932 { |
| 933 // CSS property filters are inefficient so we need to make sure that |
| 934 // they're only applied if they specify active domains |
| 935 if (!/,[^~][^,.]*\.[^,]/.test("," + domain)) |
| 936 return new InvalidFilter(text, Utils.getString("filter_cssproperty_nodomai
n")); |
| 937 |
| 938 return new CSSPropertyFilter(text, domain, selector, match[2], |
| 939 selector.substr(0, match.index), |
| 940 selector.substr(match.index + match[0].length)); |
| 941 } |
| 942 |
| 943 return new ElemHideFilter(text, domain, selector); |
916 }; | 944 }; |
917 | 945 |
918 /** | 946 /** |
919 * Class for element hiding filters | 947 * Class for element hiding filters |
920 * @param {String} text see Filter() | 948 * @param {String} text see Filter() |
921 * @param {String} domains see ElemHideBase() | 949 * @param {String} domains see ElemHideBase() |
922 * @param {String} selector see ElemHideBase() | 950 * @param {String} selector see ElemHideBase() |
923 * @constructor | 951 * @constructor |
924 * @augments ElemHideBase | 952 * @augments ElemHideBase |
925 */ | 953 */ |
(...skipping 19 matching lines...) Expand all Loading... |
945 function ElemHideException(text, domains, selector) | 973 function ElemHideException(text, domains, selector) |
946 { | 974 { |
947 ElemHideBase.call(this, text, domains, selector); | 975 ElemHideBase.call(this, text, domains, selector); |
948 } | 976 } |
949 exports.ElemHideException = ElemHideException; | 977 exports.ElemHideException = ElemHideException; |
950 | 978 |
951 ElemHideException.prototype = | 979 ElemHideException.prototype = |
952 { | 980 { |
953 __proto__: ElemHideBase.prototype | 981 __proto__: ElemHideBase.prototype |
954 }; | 982 }; |
| 983 |
| 984 /** |
| 985 * Class for CSS property filters |
| 986 * @param {String} text see Filter() |
| 987 * @param {String} domains see ElemHideBase() |
| 988 * @param {String} selector see ElemHideBase() |
| 989 * @param {String} regexpSource see CSSPropertyFilter.regexpSource |
| 990 * @param {String} selectorPrefix see CSSPropertyFilter.selectorPrefix |
| 991 * @param {String} selectorSuffix see CSSPropertyFilter.selectorSuffix |
| 992 * @constructor |
| 993 * @augments ElemHideBase |
| 994 */ |
| 995 function CSSPropertyFilter(text, domains, selector, regexpSource, |
| 996 selectorPrefix, selectorSuffix) |
| 997 { |
| 998 ElemHideBase.call(this, text, domains, selector); |
| 999 |
| 1000 this.regexpSource = regexpSource; |
| 1001 this.selectorPrefix = selectorPrefix; |
| 1002 this.selectorSuffix = selectorSuffix; |
| 1003 } |
| 1004 exports.CSSPropertyFilter = CSSPropertyFilter; |
| 1005 |
| 1006 CSSPropertyFilter.prototype = |
| 1007 { |
| 1008 __proto__: ElemHideBase.prototype, |
| 1009 |
| 1010 /** |
| 1011 * Expression from which a regular expression should be generated for matching |
| 1012 * CSS properties - for delayed creation of the regexpString property |
| 1013 * @type String |
| 1014 */ |
| 1015 regexpSource: null, |
| 1016 /** |
| 1017 * Substring of CSS selector before properties for the HTML elements that |
| 1018 * should be hidden |
| 1019 * @type String |
| 1020 */ |
| 1021 selectorPrefix: null, |
| 1022 /** |
| 1023 * Substring of CSS selector after properties for the HTML elements that |
| 1024 * should be hidden |
| 1025 * @type String |
| 1026 */ |
| 1027 selectorSuffix: null, |
| 1028 |
| 1029 /** |
| 1030 * Raw regular expression string to be used when testing CSS properties |
| 1031 * against this filter |
| 1032 * @type String |
| 1033 */ |
| 1034 get regexpString() |
| 1035 { |
| 1036 // Despite this property being cached, the getter is called |
| 1037 // several times on Safari, due to WebKit bug 132872 |
| 1038 let prop = Object.getOwnPropertyDescriptor(this, "regexpString"); |
| 1039 if (prop) |
| 1040 return prop.value; |
| 1041 |
| 1042 let regexp = Filter.toRegExp(this.regexpSource); |
| 1043 Object.defineProperty(this, "regexpString", {value: regexp}); |
| 1044 return regexp; |
| 1045 } |
| 1046 }; |
LEFT | RIGHT |