mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-11 17:41:03 +01:00
351 lines
10 KiB
JavaScript
351 lines
10 KiB
JavaScript
/*******************************************************************************
|
|
|
|
uBlock Origin - a Chromium browser extension to black/white list requests.
|
|
Copyright (C) 2015-2017 Raymond Hill
|
|
|
|
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://github.com/gorhill/uBlock
|
|
*/
|
|
|
|
/* global punycode */
|
|
/* jshint bitwise: false */
|
|
|
|
'use strict';
|
|
|
|
/******************************************************************************/
|
|
|
|
µBlock.HnSwitches = (function() {
|
|
|
|
/******************************************************************************/
|
|
|
|
var HnSwitches = function() {
|
|
this.reset();
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
var switchBitOffsets = {
|
|
'no-strict-blocking': 0,
|
|
'no-popups': 2,
|
|
'no-cosmetic-filtering': 4,
|
|
'no-remote-fonts': 6,
|
|
'no-large-media': 8
|
|
};
|
|
|
|
var fromLegacySwitchNames = {
|
|
'dontBlockDoc': 'no-strict-blocking',
|
|
'doBlockAllPopups': 'no-popups',
|
|
'noStrictBlocking': 'no-strict-blocking',
|
|
'noPopups': 'no-popups',
|
|
'noCosmeticFiltering': 'no-cosmetic-filtering'
|
|
};
|
|
|
|
var switchStateToNameMap = {
|
|
'1': 'true',
|
|
'2': 'false'
|
|
};
|
|
|
|
var nameToSwitchStateMap = {
|
|
'true': 1,
|
|
'false': 2,
|
|
'on': 1,
|
|
'off': 2
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
// For performance purpose, as simple tests as possible
|
|
var reHostnameVeryCoarse = /[g-z_-]/;
|
|
var reIPv4VeryCoarse = /\.\d+$/;
|
|
|
|
// http://tools.ietf.org/html/rfc5952
|
|
// 4.3: "MUST be represented in lowercase"
|
|
// Also: http://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers
|
|
|
|
var isIPAddress = function(hostname) {
|
|
if ( reHostnameVeryCoarse.test(hostname) ) {
|
|
return false;
|
|
}
|
|
if ( reIPv4VeryCoarse.test(hostname) ) {
|
|
return true;
|
|
}
|
|
return hostname.startsWith('[');
|
|
};
|
|
|
|
var toBroaderHostname = function(hostname) {
|
|
var pos = hostname.indexOf('.');
|
|
if ( pos !== -1 ) {
|
|
return hostname.slice(pos + 1);
|
|
}
|
|
return hostname !== '*' && hostname !== '' ? '*' : '';
|
|
};
|
|
|
|
var toBroaderIPAddress = function(ipaddress) {
|
|
return ipaddress !== '*' && ipaddress !== '' ? '*' : '';
|
|
};
|
|
|
|
var selectHostnameBroadener = function(hostname) {
|
|
return isIPAddress(hostname) ? toBroaderIPAddress : toBroaderHostname;
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
HnSwitches.prototype.reset = function() {
|
|
this.switches = {};
|
|
this.n = '';
|
|
this.z = '';
|
|
this.r = 0;
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
// If value is undefined, the switch is removed
|
|
|
|
HnSwitches.prototype.toggle = function(switchName, hostname, newVal) {
|
|
var bitOffset = switchBitOffsets[switchName];
|
|
if ( bitOffset === undefined ) {
|
|
return false;
|
|
}
|
|
if ( newVal === this.evaluate(switchName, hostname) ) {
|
|
return false;
|
|
}
|
|
var bits = this.switches[hostname] || 0;
|
|
bits &= ~(3 << bitOffset);
|
|
bits |= newVal << bitOffset;
|
|
if ( bits === 0 ) {
|
|
delete this.switches[hostname];
|
|
} else {
|
|
this.switches[hostname] = bits;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
HnSwitches.prototype.toggleOneZ = function(switchName, hostname, newState) {
|
|
var bitOffset = switchBitOffsets[switchName];
|
|
if ( bitOffset === undefined ) {
|
|
return false;
|
|
}
|
|
var state = this.evaluateZ(switchName, hostname);
|
|
if ( newState === state ) {
|
|
return false;
|
|
}
|
|
if ( newState === undefined ) {
|
|
newState = !state;
|
|
}
|
|
var bits = this.switches[hostname] || 0;
|
|
bits &= ~(3 << bitOffset);
|
|
if ( bits === 0 ) {
|
|
delete this.switches[hostname];
|
|
} else {
|
|
this.switches[hostname] = bits;
|
|
}
|
|
state = this.evaluateZ(switchName, hostname);
|
|
if ( state === newState ) {
|
|
return true;
|
|
}
|
|
this.switches[hostname] = bits | ((newState ? 1 : 2) << bitOffset);
|
|
return true;
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
HnSwitches.prototype.toggleBranchZ = function(switchName, targetHostname, newState) {
|
|
var changed = this.toggleOneZ(switchName, targetHostname, newState);
|
|
var targetLen = targetHostname.length;
|
|
|
|
// Turn off all descendant switches, they will inherit the state of the
|
|
// branch's origin.
|
|
for ( var hostname in this.switches ) {
|
|
if ( this.switches.hasOwnProperty(hostname) === false ) {
|
|
continue;
|
|
}
|
|
if ( hostname === targetHostname ) {
|
|
continue;
|
|
}
|
|
if ( hostname.length <= targetLen ) {
|
|
continue;
|
|
}
|
|
if ( hostname.endsWith(targetHostname) === false ) {
|
|
continue;
|
|
}
|
|
if ( hostname.charAt(hostname.length - targetLen - 1) !== '.' ) {
|
|
continue;
|
|
}
|
|
changed = this.toggle(switchName, hostname, 0) || changed;
|
|
}
|
|
|
|
return changed;
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
HnSwitches.prototype.toggleZ = function(switchName, hostname, deep, newState) {
|
|
if ( deep === true ) {
|
|
return this.toggleBranchZ(switchName, hostname, newState);
|
|
}
|
|
return this.toggleOneZ(switchName, hostname, newState);
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
// 0 = inherit from broader scope, up to default state
|
|
// 1 = non-default state
|
|
// 2 = forced default state (to override a broader non-default state)
|
|
|
|
HnSwitches.prototype.evaluate = function(switchName, hostname) {
|
|
var bits = this.switches[hostname] || 0;
|
|
if ( bits === 0 ) {
|
|
return 0;
|
|
}
|
|
var bitOffset = switchBitOffsets[switchName];
|
|
if ( bitOffset === undefined ) {
|
|
return 0;
|
|
}
|
|
return (bits >>> bitOffset) & 3;
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
HnSwitches.prototype.evaluateZ = function(switchName, hostname) {
|
|
var bitOffset = switchBitOffsets[switchName];
|
|
if ( bitOffset === undefined ) {
|
|
this.r = 0;
|
|
return false;
|
|
}
|
|
this.n = switchName;
|
|
var bits,
|
|
hn = hostname,
|
|
broadenSource = selectHostnameBroadener(hn);
|
|
for (;;) {
|
|
bits = this.switches[hn] || 0;
|
|
if ( bits !== 0 ) {
|
|
bits = bits >>> bitOffset & 3;
|
|
if ( bits !== 0 ) {
|
|
this.z = hn;
|
|
this.r = bits;
|
|
return bits === 1;
|
|
}
|
|
}
|
|
hn = broadenSource(hn);
|
|
if ( hn === '' ) { break; }
|
|
}
|
|
this.r = 0;
|
|
return false;
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
HnSwitches.prototype.toLogData = function() {
|
|
return {
|
|
source: 'switch',
|
|
result: this.r,
|
|
raw: this.n + ': ' + this.z + ' true'
|
|
};
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
HnSwitches.prototype.toString = function() {
|
|
var out = [],
|
|
switchName, val,
|
|
hostname,
|
|
toUnicode = punycode.toUnicode;
|
|
for ( hostname in this.switches ) {
|
|
if ( this.switches.hasOwnProperty(hostname) === false ) {
|
|
continue;
|
|
}
|
|
for ( switchName in switchBitOffsets ) {
|
|
if ( switchBitOffsets.hasOwnProperty(switchName) === false ) {
|
|
continue;
|
|
}
|
|
val = this.evaluate(switchName, hostname);
|
|
if ( val === 0 ) { continue; }
|
|
if ( hostname.indexOf('xn--') !== -1 ) {
|
|
hostname = toUnicode(hostname);
|
|
}
|
|
out.push(switchName + ': ' + hostname + ' ' + switchStateToNameMap[val]);
|
|
}
|
|
}
|
|
return out.join('\n');
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
HnSwitches.prototype.fromString = function(text) {
|
|
var lineIter = new µBlock.LineIterator(text),
|
|
line, pos, fields,
|
|
switchName, hostname, state,
|
|
reNotASCII = /[^\x20-\x7F]/,
|
|
toASCII = punycode.toASCII;
|
|
|
|
this.reset();
|
|
|
|
while ( lineIter.eot() === false ) {
|
|
line = lineIter.next().trim();
|
|
pos = line.indexOf('# ');
|
|
if ( pos !== -1 ) {
|
|
line = line.slice(0, pos).trim();
|
|
}
|
|
if ( line === '' ) {
|
|
continue;
|
|
}
|
|
|
|
fields = line.split(/\s+/);
|
|
if ( fields.length !== 3 ) {
|
|
continue;
|
|
}
|
|
|
|
switchName = fields[0];
|
|
pos = switchName.indexOf(':');
|
|
if ( pos === -1 ) {
|
|
continue;
|
|
}
|
|
switchName = switchName.slice(0, pos);
|
|
switchName = fromLegacySwitchNames[switchName] || switchName;
|
|
if ( switchBitOffsets.hasOwnProperty(switchName) === false ) {
|
|
continue;
|
|
}
|
|
|
|
// Performance: avoid punycoding if hostname is made only of
|
|
// ASCII characters.
|
|
hostname = fields[1];
|
|
if ( reNotASCII.test(hostname) ) {
|
|
hostname = toASCII(hostname);
|
|
}
|
|
|
|
state = fields[2];
|
|
if ( nameToSwitchStateMap.hasOwnProperty(state) === false ) {
|
|
continue;
|
|
}
|
|
|
|
this.toggle(switchName, hostname, nameToSwitchStateMap[state]);
|
|
}
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
return HnSwitches;
|
|
|
|
/******************************************************************************/
|
|
|
|
})();
|
|
|
|
/******************************************************************************/
|
|
|
|
µBlock.hnSwitches = new µBlock.HnSwitches();
|
|
|
|
/******************************************************************************/
|