/******************************************************************************* ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors Copyright (C) 2019-2020 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://libregit.spks.xyz/heckyel/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