|
|
@@ -0,0 +1,792 @@ |
|
|
|
// Generated by gfwlist2pac in precise mode
|
|
|
|
// https://github.com/clowwindy/gfwlist2pac
|
|
|
|
|
|
|
|
// 2019-02-08: Updated to support shadowsocks-windows user rules.
|
|
|
|
|
|
|
|
var proxy = "__PROXY__";
|
|
|
|
|
|
|
|
var userrules = __USERRULES__;
|
|
|
|
var rules = __RULES__;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is part of Adblock Plus <http://adblockplus.org/>,
|
|
|
|
* Copyright (C) 2006-2014 Eyeo GmbH
|
|
|
|
*
|
|
|
|
* Adblock Plus is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 3 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* Adblock Plus is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
function createDict()
|
|
|
|
{
|
|
|
|
var result = {};
|
|
|
|
result.__proto__ = null;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getOwnPropertyDescriptor(obj, key)
|
|
|
|
{
|
|
|
|
if (obj.hasOwnProperty(key))
|
|
|
|
{
|
|
|
|
return obj[key];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function extend(subclass, superclass, definition)
|
|
|
|
{
|
|
|
|
if (Object.__proto__)
|
|
|
|
{
|
|
|
|
definition.__proto__ = superclass.prototype;
|
|
|
|
subclass.prototype = definition;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var tmpclass = function(){}, ret;
|
|
|
|
tmpclass.prototype = superclass.prototype;
|
|
|
|
subclass.prototype = new tmpclass();
|
|
|
|
subclass.prototype.constructor = superclass;
|
|
|
|
for (var i in definition)
|
|
|
|
{
|
|
|
|
if (definition.hasOwnProperty(i))
|
|
|
|
{
|
|
|
|
subclass.prototype[i] = definition[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function Filter(text)
|
|
|
|
{
|
|
|
|
this.text = text;
|
|
|
|
this.subscriptions = [];
|
|
|
|
}
|
|
|
|
Filter.prototype = {
|
|
|
|
text: null,
|
|
|
|
subscriptions: null,
|
|
|
|
toString: function()
|
|
|
|
{
|
|
|
|
return this.text;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Filter.knownFilters = createDict();
|
|
|
|
Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/;
|
|
|
|
Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/;
|
|
|
|
Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/;
|
|
|
|
Filter.fromText = function(text)
|
|
|
|
{
|
|
|
|
if (text in Filter.knownFilters)
|
|
|
|
{
|
|
|
|
return Filter.knownFilters[text];
|
|
|
|
}
|
|
|
|
var ret;
|
|
|
|
if (text.charAt(0) == "!")
|
|
|
|
{
|
|
|
|
ret = new CommentFilter(text);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = RegExpFilter.fromText(text);
|
|
|
|
}
|
|
|
|
Filter.knownFilters[ret.text] = ret;
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
function InvalidFilter(text, reason)
|
|
|
|
{
|
|
|
|
Filter.call(this, text);
|
|
|
|
this.reason = reason;
|
|
|
|
}
|
|
|
|
extend(InvalidFilter, Filter, {
|
|
|
|
reason: null
|
|
|
|
});
|
|
|
|
|
|
|
|
function CommentFilter(text)
|
|
|
|
{
|
|
|
|
Filter.call(this, text);
|
|
|
|
}
|
|
|
|
extend(CommentFilter, Filter, {
|
|
|
|
});
|
|
|
|
|
|
|
|
function ActiveFilter(text, domains)
|
|
|
|
{
|
|
|
|
Filter.call(this, text);
|
|
|
|
this.domainSource = domains;
|
|
|
|
}
|
|
|
|
extend(ActiveFilter, Filter, {
|
|
|
|
domainSource: null,
|
|
|
|
domainSeparator: null,
|
|
|
|
ignoreTrailingDot: true,
|
|
|
|
domainSourceIsUpperCase: false,
|
|
|
|
getDomains: function()
|
|
|
|
{
|
|
|
|
var prop = getOwnPropertyDescriptor(this, "domains");
|
|
|
|
if (prop)
|
|
|
|
{
|
|
|
|
return prop;
|
|
|
|
}
|
|
|
|
var domains = null;
|
|
|
|
if (this.domainSource)
|
|
|
|
{
|
|
|
|
var source = this.domainSource;
|
|
|
|
if (!this.domainSourceIsUpperCase)
|
|
|
|
{
|
|
|
|
source = source.toUpperCase();
|
|
|
|
}
|
|
|
|
var list = source.split(this.domainSeparator);
|
|
|
|
if (list.length == 1 && (list[0]).charAt(0) != "~")
|
|
|
|
{
|
|
|
|
domains = createDict();
|
|
|
|
domains[""] = false;
|
|
|
|
if (this.ignoreTrailingDot)
|
|
|
|
{
|
|
|
|
list[0] = list[0].replace(/\.+$/, "");
|
|
|
|
}
|
|
|
|
domains[list[0]] = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var hasIncludes = false;
|
|
|
|
for (var i = 0; i < list.length; i++)
|
|
|
|
{
|
|
|
|
var domain = list[i];
|
|
|
|
if (this.ignoreTrailingDot)
|
|
|
|
{
|
|
|
|
domain = domain.replace(/\.+$/, "");
|
|
|
|
}
|
|
|
|
if (domain == "")
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var include;
|
|
|
|
if (domain.charAt(0) == "~")
|
|
|
|
{
|
|
|
|
include = false;
|
|
|
|
domain = domain.substr(1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
include = true;
|
|
|
|
hasIncludes = true;
|
|
|
|
}
|
|
|
|
if (!domains)
|
|
|
|
{
|
|
|
|
domains = createDict();
|
|
|
|
}
|
|
|
|
domains[domain] = include;
|
|
|
|
}
|
|
|
|
domains[""] = !hasIncludes;
|
|
|
|
}
|
|
|
|
this.domainSource = null;
|
|
|
|
}
|
|
|
|
return this.domains;
|
|
|
|
},
|
|
|
|
sitekeys: null,
|
|
|
|
isActiveOnDomain: function(docDomain, sitekey)
|
|
|
|
{
|
|
|
|
if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!this.getDomains())
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!docDomain)
|
|
|
|
{
|
|
|
|
return this.getDomains()[""];
|
|
|
|
}
|
|
|
|
if (this.ignoreTrailingDot)
|
|
|
|
{
|
|
|
|
docDomain = docDomain.replace(/\.+$/, "");
|
|
|
|
}
|
|
|
|
docDomain = docDomain.toUpperCase();
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (docDomain in this.getDomains())
|
|
|
|
{
|
|
|
|
return this.domains[docDomain];
|
|
|
|
}
|
|
|
|
var nextDot = docDomain.indexOf(".");
|
|
|
|
if (nextDot < 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
docDomain = docDomain.substr(nextDot + 1);
|
|
|
|
}
|
|
|
|
return this.domains[""];
|
|
|
|
},
|
|
|
|
isActiveOnlyOnDomain: function(docDomain)
|
|
|
|
{
|
|
|
|
if (!docDomain || !this.getDomains() || this.getDomains()[""])
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this.ignoreTrailingDot)
|
|
|
|
{
|
|
|
|
docDomain = docDomain.replace(/\.+$/, "");
|
|
|
|
}
|
|
|
|
docDomain = docDomain.toUpperCase();
|
|
|
|
for (var domain in this.getDomains())
|
|
|
|
{
|
|
|
|
if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys)
|
|
|
|
{
|
|
|
|
ActiveFilter.call(this, text, domains, sitekeys);
|
|
|
|
if (contentType != null)
|
|
|
|
{
|
|
|
|
this.contentType = contentType;
|
|
|
|
}
|
|
|
|
if (matchCase)
|
|
|
|
{
|
|
|
|
this.matchCase = matchCase;
|
|
|
|
}
|
|
|
|
if (thirdParty != null)
|
|
|
|
{
|
|
|
|
this.thirdParty = thirdParty;
|
|
|
|
}
|
|
|
|
if (sitekeys != null)
|
|
|
|
{
|
|
|
|
this.sitekeySource = sitekeys;
|
|
|
|
}
|
|
|
|
if (regexpSource.length >= 2 && regexpSource.charAt(0) == "/" && regexpSource.charAt(regexpSource.length - 1) == "/")
|
|
|
|
{
|
|
|
|
var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i");
|
|
|
|
this.regexp = regexp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.regexpSource = regexpSource;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
extend(RegExpFilter, ActiveFilter, {
|
|
|
|
domainSourceIsUpperCase: true,
|
|
|
|
length: 1,
|
|
|
|
domainSeparator: "|",
|
|
|
|
regexpSource: null,
|
|
|
|
getRegexp: function()
|
|
|
|
{
|
|
|
|
var prop = getOwnPropertyDescriptor(this, "regexp");
|
|
|
|
if (prop)
|
|
|
|
{
|
|
|
|
return prop;
|
|
|
|
}
|
|
|
|
var source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, "");
|
|
|
|
var regexp = new RegExp(source, this.matchCase ? "" : "i");
|
|
|
|
this.regexp = regexp;
|
|
|
|
return regexp;
|
|
|
|
},
|
|
|
|
contentType: 2147483647,
|
|
|
|
matchCase: false,
|
|
|
|
thirdParty: null,
|
|
|
|
sitekeySource: null,
|
|
|
|
getSitekeys: function()
|
|
|
|
{
|
|
|
|
var prop = getOwnPropertyDescriptor(this, "sitekeys");
|
|
|
|
if (prop)
|
|
|
|
{
|
|
|
|
return prop;
|
|
|
|
}
|
|
|
|
var sitekeys = null;
|
|
|
|
if (this.sitekeySource)
|
|
|
|
{
|
|
|
|
sitekeys = this.sitekeySource.split("|");
|
|
|
|
this.sitekeySource = null;
|
|
|
|
}
|
|
|
|
this.sitekeys = sitekeys;
|
|
|
|
return this.sitekeys;
|
|
|
|
},
|
|
|
|
matches: function(location, contentType, docDomain, thirdParty, sitekey)
|
|
|
|
{
|
|
|
|
if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
RegExpFilter.prototype["0"] = "#this";
|
|
|
|
RegExpFilter.fromText = function(text)
|
|
|
|
{
|
|
|
|
var blocking = true;
|
|
|
|
var origText = text;
|
|
|
|
if (text.indexOf("@@") == 0)
|
|
|
|
{
|
|
|
|
blocking = false;
|
|
|
|
text = text.substr(2);
|
|
|
|
}
|
|
|
|
var contentType = null;
|
|
|
|
var matchCase = null;
|
|
|
|
var domains = null;
|
|
|
|
var sitekeys = null;
|
|
|
|
var thirdParty = null;
|
|
|
|
var collapse = null;
|
|
|
|
var options;
|
|
|
|
var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null;
|
|
|
|
if (match)
|
|
|
|
{
|
|
|
|
options = match[1].toUpperCase().split(",");
|
|
|
|
text = match.input.substr(0, match.index);
|
|
|
|
for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6)
|
|
|
|
{
|
|
|
|
var option = options[_loopIndex6];
|
|
|
|
var value = null;
|
|
|
|
var separatorIndex = option.indexOf("=");
|
|
|
|
if (separatorIndex >= 0)
|
|
|
|
{
|
|
|
|
value = option.substr(separatorIndex + 1);
|
|
|
|
option = option.substr(0, separatorIndex);
|
|
|
|
}
|
|
|
|
option = option.replace(/-/, "_");
|
|
|
|
if (option in RegExpFilter.typeMap)
|
|
|
|
{
|
|
|
|
if (contentType == null)
|
|
|
|
{
|
|
|
|
contentType = 0;
|
|
|
|
}
|
|
|
|
contentType |= RegExpFilter.typeMap[option];
|
|
|
|
}
|
|
|
|
else if (option.charAt(0) == "~" && option.substr(1) in RegExpFilter.typeMap)
|
|
|
|
{
|
|
|
|
if (contentType == null)
|
|
|
|
{
|
|
|
|
contentType = RegExpFilter.prototype.contentType;
|
|
|
|
}
|
|
|
|
contentType &= ~RegExpFilter.typeMap[option.substr(1)];
|
|
|
|
}
|
|
|
|
else if (option == "MATCH_CASE")
|
|
|
|
{
|
|
|
|
matchCase = true;
|
|
|
|
}
|
|
|
|
else if (option == "~MATCH_CASE")
|
|
|
|
{
|
|
|
|
matchCase = false;
|
|
|
|
}
|
|
|
|
else if (option == "DOMAIN" && typeof value != "undefined")
|
|
|
|
{
|
|
|
|
domains = value;
|
|
|
|
}
|
|
|
|
else if (option == "THIRD_PARTY")
|
|
|
|
{
|
|
|
|
thirdParty = true;
|
|
|
|
}
|
|
|
|
else if (option == "~THIRD_PARTY")
|
|
|
|
{
|
|
|
|
thirdParty = false;
|
|
|
|
}
|
|
|
|
else if (option == "COLLAPSE")
|
|
|
|
{
|
|
|
|
collapse = true;
|
|
|
|
}
|
|
|
|
else if (option == "~COLLAPSE")
|
|
|
|
{
|
|
|
|
collapse = false;
|
|
|
|
}
|
|
|
|
else if (option == "SITEKEY" && typeof value != "undefined")
|
|
|
|
{
|
|
|
|
sitekeys = value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return new InvalidFilter(origText, "Unknown option " + option.toLowerCase());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text))
|
|
|
|
{
|
|
|
|
if (contentType == null)
|
|
|
|
{
|
|
|
|
contentType = RegExpFilter.prototype.contentType;
|
|
|
|
}
|
|
|
|
contentType &= ~RegExpFilter.typeMap.DOCUMENT;
|
|
|
|
}
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (blocking)
|
|
|
|
{
|
|
|
|
return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (e)
|
|
|
|
{
|
|
|
|
return new InvalidFilter(origText, e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
RegExpFilter.typeMap = {
|
|
|
|
OTHER: 1,
|
|
|
|
SCRIPT: 2,
|
|
|
|
IMAGE: 4,
|
|
|
|
STYLESHEET: 8,
|
|
|
|
OBJECT: 16,
|
|
|
|
SUBDOCUMENT: 32,
|
|
|
|
DOCUMENT: 64,
|
|
|
|
XBL: 1,
|
|
|
|
PING: 1,
|
|
|
|
XMLHTTPREQUEST: 2048,
|
|
|
|
OBJECT_SUBREQUEST: 4096,
|
|
|
|
DTD: 1,
|
|
|
|
MEDIA: 16384,
|
|
|
|
FONT: 32768,
|
|
|
|
BACKGROUND: 4,
|
|
|
|
POPUP: 268435456,
|
|
|
|
ELEMHIDE: 1073741824
|
|
|
|
};
|
|
|
|
RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP);
|
|
|
|
|
|
|
|
function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse)
|
|
|
|
{
|
|
|
|
RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys);
|
|
|
|
this.collapse = collapse;
|
|
|
|
}
|
|
|
|
extend(BlockingFilter, RegExpFilter, {
|
|
|
|
collapse: null
|
|
|
|
});
|
|
|
|
|
|
|
|
function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys)
|
|
|
|
{
|
|
|
|
RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys);
|
|
|
|
}
|
|
|
|
extend(WhitelistFilter, RegExpFilter, {
|
|
|
|
});
|
|
|
|
|
|
|
|
function Matcher()
|
|
|
|
{
|
|
|
|
this.clear();
|
|
|
|
}
|
|
|
|
Matcher.prototype = {
|
|
|
|
filterByKeyword: null,
|
|
|
|
keywordByFilter: null,
|
|
|
|
clear: function()
|
|
|
|
{
|
|
|
|
this.filterByKeyword = createDict();
|
|
|
|
this.keywordByFilter = createDict();
|
|
|
|
},
|
|
|
|
add: function(filter)
|
|
|
|
{
|
|
|
|
if (filter.text in this.keywordByFilter)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var keyword = this.findKeyword(filter);
|
|
|
|
var oldEntry = this.filterByKeyword[keyword];
|
|
|
|
if (typeof oldEntry == "undefined")
|
|
|
|
{
|
|
|
|
this.filterByKeyword[keyword] = filter;
|
|
|
|
}
|
|
|
|
else if (oldEntry.length == 1)
|
|
|
|
{
|
|
|
|
this.filterByKeyword[keyword] = [oldEntry, filter];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
oldEntry.push(filter);
|
|
|
|
}
|
|
|
|
this.keywordByFilter[filter.text] = keyword;
|
|
|
|
},
|
|
|
|
remove: function(filter)
|
|
|
|
{
|
|
|
|
if (!(filter.text in this.keywordByFilter))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var keyword = this.keywordByFilter[filter.text];
|
|
|
|
var list = this.filterByKeyword[keyword];
|
|
|
|
if (list.length <= 1)
|
|
|
|
{
|
|
|
|
delete this.filterByKeyword[keyword];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var index = list.indexOf(filter);
|
|
|
|
if (index >= 0)
|
|
|
|
{
|
|
|
|
list.splice(index, 1);
|
|
|
|
if (list.length == 1)
|
|
|
|
{
|
|
|
|
this.filterByKeyword[keyword] = list[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete this.keywordByFilter[filter.text];
|
|
|
|
},
|
|
|
|
findKeyword: function(filter)
|
|
|
|
{
|
|
|
|
var result = "";
|
|
|
|
var text = filter.text;
|
|
|
|
if (Filter.regexpRegExp.test(text))
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
var match = Filter.optionsRegExp.exec(text);
|
|
|
|
if (match)
|
|
|
|
{
|
|
|
|
text = match.input.substr(0, match.index);
|
|
|
|
}
|
|
|
|
if (text.substr(0, 2) == "@@")
|
|
|
|
{
|
|
|
|
text = text.substr(2);
|
|
|
|
}
|
|
|
|
var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g);
|
|
|
|
if (!candidates)
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
var hash = this.filterByKeyword;
|
|
|
|
var resultCount = 16777215;
|
|
|
|
var resultLength = 0;
|
|
|
|
for (var i = 0, l = candidates.length; i < l; i++)
|
|
|
|
{
|
|
|
|
var candidate = candidates[i].substr(1);
|
|
|
|
var count = candidate in hash ? hash[candidate].length : 0;
|
|
|
|
if (count < resultCount || count == resultCount && candidate.length > resultLength)
|
|
|
|
{
|
|
|
|
result = candidate;
|
|
|
|
resultCount = count;
|
|
|
|
resultLength = candidate.length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
hasFilter: function(filter)
|
|
|
|
{
|
|
|
|
return filter.text in this.keywordByFilter;
|
|
|
|
},
|
|
|
|
getKeywordForFilter: function(filter)
|
|
|
|
{
|
|
|
|
if (filter.text in this.keywordByFilter)
|
|
|
|
{
|
|
|
|
return this.keywordByFilter[filter.text];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey)
|
|
|
|
{
|
|
|
|
var list = this.filterByKeyword[keyword];
|
|
|
|
for (var i = 0; i < list.length; i++)
|
|
|
|
{
|
|
|
|
var filter = list[i];
|
|
|
|
if (filter == "#this")
|
|
|
|
{
|
|
|
|
filter = list;
|
|
|
|
}
|
|
|
|
if (filter.matches(location, contentType, docDomain, thirdParty, sitekey))
|
|
|
|
{
|
|
|
|
return filter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
matchesAny: function(location, contentType, docDomain, thirdParty, sitekey)
|
|
|
|
{
|
|
|
|
var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g);
|
|
|
|
if (candidates === null)
|
|
|
|
{
|
|
|
|
candidates = [];
|
|
|
|
}
|
|
|
|
candidates.push("");
|
|
|
|
for (var i = 0, l = candidates.length; i < l; i++)
|
|
|
|
{
|
|
|
|
var substr = candidates[i];
|
|
|
|
if (substr in this.filterByKeyword)
|
|
|
|
{
|
|
|
|
var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey);
|
|
|
|
if (result)
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function CombinedMatcher()
|
|
|
|
{
|
|
|
|
this.blacklist = new Matcher();
|
|
|
|
this.whitelist = new Matcher();
|
|
|
|
this.resultCache = createDict();
|
|
|
|
}
|
|
|
|
CombinedMatcher.maxCacheEntries = 1000;
|
|
|
|
CombinedMatcher.prototype = {
|
|
|
|
blacklist: null,
|
|
|
|
whitelist: null,
|
|
|
|
resultCache: null,
|
|
|
|
cacheEntries: 0,
|
|
|
|
clear: function()
|
|
|
|
{
|
|
|
|
this.blacklist.clear();
|
|
|
|
this.whitelist.clear();
|
|
|
|
this.resultCache = createDict();
|
|
|
|
this.cacheEntries = 0;
|
|
|
|
},
|
|
|
|
add: function(filter)
|
|
|
|
{
|
|
|
|
if (filter instanceof WhitelistFilter)
|
|
|
|
{
|
|
|
|
this.whitelist.add(filter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.blacklist.add(filter);
|
|
|
|
}
|
|
|
|
if (this.cacheEntries > 0)
|
|
|
|
{
|
|
|
|
this.resultCache = createDict();
|
|
|
|
this.cacheEntries = 0;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
remove: function(filter)
|
|
|
|
{
|
|
|
|
if (filter instanceof WhitelistFilter)
|
|
|
|
{
|
|
|
|
this.whitelist.remove(filter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.blacklist.remove(filter);
|
|
|
|
}
|
|
|
|
if (this.cacheEntries > 0)
|
|
|
|
{
|
|
|
|
this.resultCache = createDict();
|
|
|
|
this.cacheEntries = 0;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
findKeyword: function(filter)
|
|
|
|
{
|
|
|
|
if (filter instanceof WhitelistFilter)
|
|
|
|
{
|
|
|
|
return this.whitelist.findKeyword(filter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.blacklist.findKeyword(filter);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
hasFilter: function(filter)
|
|
|
|
{
|
|
|
|
if (filter instanceof WhitelistFilter)
|
|
|
|
{
|
|
|
|
return this.whitelist.hasFilter(filter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.blacklist.hasFilter(filter);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getKeywordForFilter: function(filter)
|
|
|
|
{
|
|
|
|
if (filter instanceof WhitelistFilter)
|
|
|
|
{
|
|
|
|
return this.whitelist.getKeywordForFilter(filter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.blacklist.getKeywordForFilter(filter);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
isSlowFilter: function(filter)
|
|
|
|
{
|
|
|
|
var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist;
|
|
|
|
if (matcher.hasFilter(filter))
|
|
|
|
{
|
|
|
|
return !matcher.getKeywordForFilter(filter);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return !matcher.findKeyword(filter);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey)
|
|
|
|
{
|
|
|
|
var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g);
|
|
|
|
if (candidates === null)
|
|
|
|
{
|
|
|
|
candidates = [];
|
|
|
|
}
|
|
|
|
candidates.push("");
|
|
|
|
var blacklistHit = null;
|
|
|
|
for (var i = 0, l = candidates.length; i < l; i++)
|
|
|
|
{
|
|
|
|
var substr = candidates[i];
|
|
|
|
if (substr in this.whitelist.filterByKeyword)
|
|
|
|
{
|
|
|
|
var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey);
|
|
|
|
if (result)
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (substr in this.blacklist.filterByKeyword && blacklistHit === null)
|
|
|
|
{
|
|
|
|
blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return blacklistHit;
|
|
|
|
},
|
|
|
|
matchesAny: function(location, docDomain)
|
|
|
|
{
|
|
|
|
var key = location + " " + docDomain + " ";
|
|
|
|
if (key in this.resultCache)
|
|
|
|
{
|
|
|
|
return this.resultCache[key];
|
|
|
|
}
|
|
|
|
var result = this.matchesAnyInternal(location, 0, docDomain, null, null);
|
|
|
|
if (this.cacheEntries >= CombinedMatcher.maxCacheEntries)
|
|
|
|
{
|
|
|
|
this.resultCache = createDict();
|
|
|
|
this.cacheEntries = 0;
|
|
|
|
}
|
|
|
|
this.resultCache[key] = result;
|
|
|
|
this.cacheEntries++;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var userrulesMatcher = new CombinedMatcher();
|
|
|
|
var defaultMatcher = new CombinedMatcher();
|
|
|
|
|
|
|
|
var direct = 'DIRECT;';
|
|
|
|
|
|
|
|
for (var i = 0; i < userrules.length; i++) {
|
|
|
|
userrulesMatcher.add(Filter.fromText(userrules[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var i = 0; i < rules.length; i++) {
|
|
|
|
defaultMatcher.add(Filter.fromText(rules[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
function FindProxyForURL(url, host) {
|
|
|
|
if (userrulesMatcher.matchesAny(url, host) instanceof BlockingFilter) {
|
|
|
|
return proxy;
|
|
|
|
}
|
|
|
|
if (userrulesMatcher.matchesAny(url, host) instanceof WhitelistFilter) {
|
|
|
|
return direct;
|
|
|
|
}
|
|
|
|
if (defaultMatcher.matchesAny(url, host) instanceof BlockingFilter) {
|
|
|
|
return proxy;
|
|
|
|
}
|
|
|
|
return direct;
|
|
|
|
}
|