| Index: lib/filterClasses.js | 
| =================================================================== | 
| --- a/lib/filterClasses.js | 
| +++ b/lib/filterClasses.js | 
| @@ -20,22 +20,29 @@ | 
| /** | 
| * @fileOverview Definition of Filter class and its subclasses. | 
| */ | 
|  | 
| const {filterNotifier} = require("./filterNotifier"); | 
| const {extend} = require("./coreUtils"); | 
| const {filterToRegExp} = require("./common"); | 
|  | 
| +let tripleAnchorRegExp = new RegExp(filterToRegExp("|||")); | 
| + | 
| /** | 
| * All known unique domain sources mapped to their parsed values. | 
| * @type {Map.<string,Map.<string,boolean>>} | 
| */ | 
| let knownDomainMaps = new Map(); | 
|  | 
| +function isLiteralPattern(pattern) | 
| +{ | 
| +  return !/[*^|]/.test(pattern.replace(/^\|{2}/, "")); | 
| +} | 
| + | 
| /** | 
| * Abstract base class for filters | 
| * | 
| * @param {string} text   string representation of the filter | 
| * @constructor | 
| */ | 
| function Filter(text) | 
| { | 
| @@ -688,16 +695,19 @@ | 
| // The filter is a regular expression - convert it immediately to | 
| // catch syntax errors | 
| let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), | 
| this.matchCase ? "" : "i"); | 
| Object.defineProperty(this, "regexp", {value: regexp}); | 
| } | 
| else | 
| { | 
| +    if (!this.matchCase && isLiteralPattern(regexpSource)) | 
| +      regexpSource = regexpSource.toLowerCase(); | 
| + | 
| // No need to convert this filter to regular expression yet, do it on demand | 
| this.pattern = regexpSource; | 
| } | 
| } | 
| exports.RegExpFilter = RegExpFilter; | 
|  | 
| RegExpFilter.prototype = extend(ActiveFilter, { | 
| /** | 
| @@ -719,20 +729,27 @@ | 
| */ | 
| pattern: null, | 
| /** | 
| * Regular expression to be used when testing against this filter | 
| * @type {RegExp} | 
| */ | 
| get regexp() | 
| { | 
| -    let source = filterToRegExp(this.pattern, this.rewrite != null); | 
| -    let regexp = new RegExp(source, this.matchCase ? "" : "i"); | 
| -    Object.defineProperty(this, "regexp", {value: regexp}); | 
| -    return regexp; | 
| +    let value = null; | 
| + | 
| +    let {pattern, rewrite} = this; | 
| +    if (rewrite != null || !isLiteralPattern(pattern)) | 
| +    { | 
| +      value = new RegExp(filterToRegExp(pattern, rewrite != null), | 
| +                         this.matchCase ? "" : "i"); | 
| +    } | 
| + | 
| +    Object.defineProperty(this, "regexp", {value}); | 
| +    return value; | 
| }, | 
| /** | 
| * Content types the filter applies to, combination of values from | 
| * RegExpFilter.typeMap | 
| * @type {number} | 
| */ | 
| contentType: 0x7FFFFFFF, | 
| /** | 
| @@ -783,17 +800,39 @@ | 
| * @param {string} [sitekey] public key provided by the document | 
| * @return {boolean} true in case of a match | 
| */ | 
| matches(location, typeMask, docDomain, thirdParty, sitekey) | 
| { | 
| return (this.contentType & typeMask) != 0 && | 
| (this.thirdParty == null || this.thirdParty == thirdParty) && | 
| this.isActiveOnDomain(docDomain, sitekey) && | 
| -           this.regexp.test(location); | 
| +           this.matchesLocation(location); | 
| +  }, | 
| + | 
| +  matchesLocation(location) | 
| +  { | 
| +    let {regexp} = this; | 
| + | 
| +    if (regexp) | 
| +      return regexp.test(location); | 
| + | 
| +    if (!this.matchCase) | 
| +      location = location.toLowerCase(); | 
| + | 
| +    let {pattern} = this; | 
| + | 
| +    if (pattern[0] == "|" && pattern[1] == "|") | 
| +    { | 
| +      let index = location.indexOf(pattern.substring(2)); | 
| +      return index != -1 && location[index] != "/" && | 
| +             tripleAnchorRegExp.test(location.substring(0, index)); | 
| +    } | 
| + | 
| +    return location.includes(pattern); | 
| } | 
| }); | 
|  | 
| /** | 
| * Yields the filter itself (required to optimize {@link Matcher}). | 
| * @yields {RegExpFilter} | 
| */ | 
| RegExpFilter.prototype[Symbol.iterator] = function*() | 
|  |