| 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 |