123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- /*******************************************************************************
- ηMatrix - a browser extension to black/white list requests.
- Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
- Copyright (C) 2019-2020-2021 Alessio Vanni
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program 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 this program. If not, see {http://www.gnu.org/licenses/}.
- Home: https://gitlab.com/vannilla/ematrix
- uMatrix Home: https://github.com/gorhill/uMatrix
- */
- 'use strict';
- var EXPORTED_SYMBOLS = ['publicSuffixList'];
- var exceptions = {};
- var rules = {};
- var magic = 'iscjsfsaolnm';
- // This value dictate how the search will be performed:
- // < cutoffLength → indexOf()
- // >= cutoffLength → binary search
- var cutoffLength = 256;
- var reMustPunycode = /[^\w.*-]/;
- var onChangedListeners = [];
- function search(store, hostname) {
- let pos = hostname.lastIndexOf('.');
- let tld;
- let remainder;
- if (pos < 0) {
- tld = hostname;
- remainder = hostname;
- } else {
- tld = hostname.slice(pos+1);
- remainder = hostname.slice(0, pos);
- }
- let sub = store[tld];
- if (!sub) {
- return false;
- }
- if (typeof sub === 'string') {
- return (sub.indexOf(' '+remainder+' ') >= 0);
- }
- let l = remainder.length;
- let val = sub[l];
- if (!val) {
- return false;
- }
- let left = 0;
- let right = Math.floor(val.length/l+0.5);
- while (left < right) {
- let i = left+right >> 1;
- let key = val.substr(l*i, l);
- if (remainder < key) {
- right = i;
- } else if (remainder > key) {
- left = i+1;
- } else {
- return true;
- }
- }
- return false;
- }
- function getPublicSuffix(hostname) {
- if (!hostname) {
- return '';
- }
- while (true) {
- let pos = hostname.indexOf('.');
- if (pos < 0) {
- return hostname;
- }
- if (search(exceptions, hostname)) {
- return hostname.slice(pos+1);
- }
- if (search(rules, hostname)) {
- return hostname;
- }
- if (search(rules, '*'+hostname.slice(pos))) {
- return hostname;
- }
- hostname = hostname.slice(pos+1);
- }
- }
- function getDomain(hostname) {
- if (!hostname || hostname.charAt(0) == '.') {
- return '';
- }
- hostname = hostname.toLowerCase();
- let suffix = getPublicSuffix(hostname);
- if (suffix === hostname) {
- return '';
- }
- let len = hostname.length-suffix.length;
- let pos = hostname.lastIndexOf('.', hostname.lastIndexOf('.', len) - 1);
- if (pos <= 0) {
- return hostname;
- }
- return hostname.slice(pos+1);
- }
- function crystallize(store) {
- for (let tld in store) {
- if (!store.hasOwnProperty(tld)) {
- continue;
- }
- let suff = store[tld].join(' ');
- if (!suff) {
- store[tld] = '';
- continue;
- }
- if (suff.length < cutoffLength) {
- store[tld] = ' ' + suff + ' ';
- continue;
- }
- suff = [];
- for (let i=store[tld].length-1; i>=0; --i) {
- let s = store[tld][i];
- let l = s.length;
- if (!suff[l]) {
- suff[l] = [];
- }
- suff[l].push(s);
- }
- for (let i=suff.length-1; i>=0; --i) {
- if (suff[i]) {
- suff[i] = suff[i].sort().join('');
- }
- }
- store[tld] = suff;
- }
- return store;
- }
- function parse(text, toAscii) {
- exceptions = {};
- rules = {};
- let beg = 0;
- let end = 0;
- let tend = text.length;
- while (beg < tend) {
- end = text.indexOf('\n', beg);
- if (end < 0) {
- end = text.indexOf('\r', beg);
- if (end < 0) {
- end = tend;
- }
- }
- let line = text.slice(beg, end).trim();
- beg = end+1;
- if (line.length === 0) {
- continue;
- }
- let pos = line.indexOf('//');
- if (pos >= 0) {
- line = line.slice(0, pos);
- }
- line = line.trim();
- if (!line) {
- continue;
- }
- let store;
- if (line.charAt(0) == '!') {
- store = exceptions;
- line = line.slice(1);
- } else {
- store = rules;
- }
- if (reMustPunycode.test(line)) {
- line = toAscii(line);
- }
- line = line.toLowerCase();
- let tld;
- pos = line.lastIndexOf('.');
- if (pos < 0) {
- tld = line;
- } else {
- tld = line.slice(pos+1);
- line = line.slice(0, pos);
- }
- if (!store.hasOwnProperty(tld)) {
- store[tld] = [];
- }
- if (line) {
- store[tld].push(line);
- }
- }
- crystallize(exceptions);
- crystallize(rules);
- callListeners(onChangedListeners);
- }
- function toSelfie() {
- return {
- magic: magic,
- rules: rules,
- exceptions: exception,
- };
- }
- function fromSelfie(selfie) {
- if (typeof selfie !== 'object' || typeof selfie.magic !== 'string'
- || selfie.magic !== magic) {
- return false;
- }
- rules = selfie.rules;
- exceptions = selfie.exceptions;
- callListeners(onChangedListeners);
- return true;
- }
- var addListener = function (listeners, callback) {
- if (typeof callback !== 'function') {
- return;
- }
- if (listeners.indexOf(callback) === -1) {
- listeners.push(callback);
- }
- };
- var removeListener = function (listeners, callback) {
- let pos = listeners.indexOf(callback);
- if (pos !== -1) {
- listeners.splice(pos, 1);
- }
- };
- var callListeners = function (listeners) {
- for (let i=0; i<listeners.length; ++i) {
- listeners[i]();
- }
- };
- var onChanged = {
- addListener: function (callback) {
- addListener(onChangedListeners, callback);
- },
- removeListener: function (callback) {
- removeListener(onChangedListeners, callback);
- },
- };
- var publicSuffixList = {
- version: '1.0',
- parse: parse,
- getDomain: getDomain,
- getPublicSuffix: getPublicSuffix,
- toSelfie: toSelfie,
- fromSelfie: fromSelfie,
- onChanged: onChanged,
- }
|