1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-06 19:02:30 +01:00

Squashed commit of the following:

commit 7c6cacc59b27660fabacb55d668ef099b222a9e6
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Sat Nov 3 08:52:51 2018 -0300

    code review: finalize support for wasm-based hntrie

commit 8596ed80e3bdac2c36e3c860b51e7189f6bc8487
Merge: cbe1f2e 000eb82
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Sat Nov 3 08:41:40 2018 -0300

    Merge branch 'master' of github.com:gorhill/uBlock into trie-wasm

commit cbe1f2e2f38484d42af3204ec7f1b5decd30f99e
Merge: 270fc7f dbb7e80
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Fri Nov 2 17:43:20 2018 -0300

    Merge branch 'master' of github.com:gorhill/uBlock into trie-wasm

commit 270fc7f9b3b73d79e6355522c1a42ce782fe7e5c
Merge: d2a89cf d693d4f
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Fri Nov 2 16:21:08 2018 -0300

    Merge branch 'master' of github.com:gorhill/uBlock into trie-wasm

commit d2a89cf28f0816ffd4617c2c7b4ccfcdcc30e1b4
Merge: d7afc78 649f82f
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Fri Nov 2 14:54:58 2018 -0300

    Merge branch 'master' of github.com:gorhill/uBlock into trie-wasm

commit d7afc78b5f5675d7d34c5a1d0ec3099a77caef49
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Fri Nov 2 13:56:11 2018 -0300

    finalize wasm-based hntrie implementation

commit e7b9e043cf36ad055791713e34eb0322dec84627
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Fri Nov 2 08:14:02 2018 -0300

    add first-pass implementation of wasm version of hntrie

commit 1015cb34624f3ef73ace58b58fe4e03dfc59897f
Author: Raymond Hill <rhill@raymondhill.net>
Date:   Wed Oct 31 17:16:47 2018 -0300

    back up draft work toward experimenting with wasm hntries
This commit is contained in:
Raymond Hill 2018-11-03 08:58:46 -03:00
parent 000eb82f08
commit d7d544cda0
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
10 changed files with 47177 additions and 668 deletions

View File

@ -33,12 +33,12 @@ if ( vAPI.webextFlavor === undefined ) {
/******************************************************************************/ /******************************************************************************/
var µBlock = (function() { // jshint ignore:line const µBlock = (function() { // jshint ignore:line
var oneSecond = 1000, const oneSecond = 1000,
oneMinute = 60 * oneSecond; oneMinute = 60 * oneSecond;
var hiddenSettingsDefault = { const hiddenSettingsDefault = {
assetFetchTimeout: 30, assetFetchTimeout: 30,
autoUpdateAssetFetchPeriod: 120, autoUpdateAssetFetchPeriod: 120,
autoUpdatePeriod: 7, autoUpdatePeriod: 7,
@ -56,7 +56,7 @@ var µBlock = (function() { // jshint ignore:line
userResourcesLocation: 'unset' userResourcesLocation: 'unset'
}; };
var whitelistDefault = [ const whitelistDefault = [
'about-scheme', 'about-scheme',
'chrome-extension-scheme', 'chrome-extension-scheme',
'chrome-scheme', 'chrome-scheme',

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,7 @@
/******************************************************************************/ /******************************************************************************/
var µb = µBlock; const µb = µBlock;
/******************************************************************************/ /******************************************************************************/
@ -287,7 +287,12 @@ var onFirstFetchReady = function(fetched) {
onVersionReady(fetched.version); onVersionReady(fetched.version);
onCommandShortcutsReady(fetched.commandShortcuts); onCommandShortcutsReady(fetched.commandShortcuts);
µb.loadPublicSuffixList(onPSLReady); Promise.all([
µb.loadPublicSuffixList(),
µb.staticNetFilteringEngine.readyToUse()
]).then(( ) => {
onPSLReady();
});
µb.loadRedirectResources(); µb.loadRedirectResources();
}; };

View File

@ -20,7 +20,7 @@
*/ */
/* jshint bitwise: false */ /* jshint bitwise: false */
/* global punycode, HNTrieBuilder */ /* global punycode, hnTrieManager */
'use strict'; 'use strict';
@ -30,7 +30,7 @@
/******************************************************************************/ /******************************************************************************/
var µb = µBlock; const µb = µBlock;
// fedcba9876543210 // fedcba9876543210
// | | ||| // | | |||
@ -43,15 +43,15 @@ var µb = µBlock;
// | +-------- bit 4- 8: type [0 - 31] // | +-------- bit 4- 8: type [0 - 31]
// +------------- bit 9-15: unused // +------------- bit 9-15: unused
var BlockAction = 0 << 0; const BlockAction = 0 << 0;
var AllowAction = 1 << 0; const AllowAction = 1 << 0;
var Important = 1 << 1; const Important = 1 << 1;
var AnyParty = 0 << 2; const AnyParty = 0 << 2;
var FirstParty = 1 << 2; const FirstParty = 1 << 2;
var ThirdParty = 2 << 2; const ThirdParty = 2 << 2;
var AnyType = 0 << 4; const AnyType = 0 << 4;
var typeNameToTypeValue = { const typeNameToTypeValue = {
'no_type': 0 << 4, 'no_type': 0 << 4,
'stylesheet': 1 << 4, 'stylesheet': 1 << 4,
'image': 2 << 4, 'image': 2 << 4,
@ -75,9 +75,9 @@ var typeNameToTypeValue = {
'webrtc': 19 << 4, 'webrtc': 19 << 4,
'unsupported': 20 << 4 'unsupported': 20 << 4
}; };
var otherTypeBitValue = typeNameToTypeValue.other; const otherTypeBitValue = typeNameToTypeValue.other;
var typeValueToTypeName = { const typeValueToTypeName = {
1: 'stylesheet', 1: 'stylesheet',
2: 'image', 2: 'image',
3: 'object', 3: 'object',
@ -100,16 +100,16 @@ var typeValueToTypeName = {
20: 'unsupported' 20: 'unsupported'
}; };
var BlockAnyTypeAnyParty = BlockAction | AnyType | AnyParty; const BlockAnyTypeAnyParty = BlockAction | AnyType | AnyParty;
var BlockAnyType = BlockAction | AnyType; const BlockAnyType = BlockAction | AnyType;
var BlockAnyParty = BlockAction | AnyParty; const BlockAnyParty = BlockAction | AnyParty;
var AllowAnyTypeAnyParty = AllowAction | AnyType | AnyParty; const AllowAnyTypeAnyParty = AllowAction | AnyType | AnyParty;
var AllowAnyType = AllowAction | AnyType; const AllowAnyType = AllowAction | AnyType;
var AllowAnyParty = AllowAction | AnyParty; const AllowAnyParty = AllowAction | AnyParty;
var genericHideException = AllowAction | AnyParty | typeNameToTypeValue.generichide, const genericHideException = AllowAction | AnyParty | typeNameToTypeValue.generichide,
genericHideImportant = BlockAction | AnyParty | typeNameToTypeValue.generichide | Important; genericHideImportant = BlockAction | AnyParty | typeNameToTypeValue.generichide | Important;
// ABP filters: https://adblockplus.org/en/filters // ABP filters: https://adblockplus.org/en/filters
// regex tester: http://regex101.com/ // regex tester: http://regex101.com/
@ -119,7 +119,7 @@ var genericHideException = AllowAction | AnyParty | typeNameToTypeValue.generich
// See the following as short-lived registers, used during evaluation. They are // See the following as short-lived registers, used during evaluation. They are
// valid until the next evaluation. // valid until the next evaluation.
var pageHostnameRegister = '', let pageHostnameRegister = '',
requestHostnameRegister = ''; requestHostnameRegister = '';
//var filterRegister = null; //var filterRegister = null;
//var categoryRegister = ''; //var categoryRegister = '';
@ -127,13 +127,13 @@ var pageHostnameRegister = '',
// Local helpers // Local helpers
// Be sure to not confuse 'example.com' with 'anotherexample.com' // Be sure to not confuse 'example.com' with 'anotherexample.com'
var isFirstParty = function(domain, hostname) { const isFirstParty = function(domain, hostname) {
return hostname.endsWith(domain) && return hostname.endsWith(domain) &&
(hostname.length === domain.length || (hostname.length === domain.length ||
hostname.charCodeAt(hostname.length - domain.length - 1) === 0x2E /* '.' */); hostname.charCodeAt(hostname.length - domain.length - 1) === 0x2E /* '.' */);
}; };
var normalizeRegexSource = function(s) { const normalizeRegexSource = function(s) {
try { try {
var re = new RegExp(s); var re = new RegExp(s);
return re.source; return re.source;
@ -143,12 +143,12 @@ var normalizeRegexSource = function(s) {
return ''; return '';
}; };
var rawToRegexStr = function(s, anchor) { const rawToRegexStr = function(s, anchor) {
var me = rawToRegexStr; let me = rawToRegexStr;
// https://www.loggly.com/blog/five-invaluable-techniques-to-improve-regex-performance/ // https://www.loggly.com/blog/five-invaluable-techniques-to-improve-regex-performance/
// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions // https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
// Also: remove leading/trailing wildcards -- there is no point. // Also: remove leading/trailing wildcards -- there is no point.
var reStr = s.replace(me.escape1, '\\$&') let reStr = s.replace(me.escape1, '\\$&')
.replace(me.escape2, '(?:[^%.0-9a-z_-]|$)') .replace(me.escape2, '(?:[^%.0-9a-z_-]|$)')
.replace(me.escape3, '') .replace(me.escape3, '')
.replace(me.escape4, '[^ ]*?'); .replace(me.escape4, '[^ ]*?');
@ -175,7 +175,7 @@ rawToRegexStr.reTextHostnameAnchor2 = '^[a-z-]+://(?:[^/?#]+)?';
const filterDataSerialize = µb.CompiledLineIO.serialize; const filterDataSerialize = µb.CompiledLineIO.serialize;
var toLogDataInternal = function(categoryBits, tokenHash, filter) { const toLogDataInternal = function(categoryBits, tokenHash, filter) {
if ( filter === null ) { return undefined; } if ( filter === null ) { return undefined; }
let logData = filter.logData(); let logData = filter.logData();
logData.compiled = filterDataSerialize([ logData.compiled = filterDataSerialize([
@ -209,7 +209,7 @@ var toLogDataInternal = function(categoryBits, tokenHash, filter) {
}; };
// First character of match must be within the hostname part of the url. // First character of match must be within the hostname part of the url.
var isHnAnchored = function(url, matchStart) { const isHnAnchored = function(url, matchStart) {
var hnStart = url.indexOf('://'); var hnStart = url.indexOf('://');
if ( hnStart === -1 ) { return false; } if ( hnStart === -1 ) { return false; }
hnStart += 3; hnStart += 3;
@ -222,9 +222,9 @@ var isHnAnchored = function(url, matchStart) {
return url.charCodeAt(matchStart - 1) === 0x2E; return url.charCodeAt(matchStart - 1) === 0x2E;
}; };
var reURLPostHostnameAnchors = /[\/?#]/; const reURLPostHostnameAnchors = /[\/?#]/;
var arrayStrictEquals = function(a, b) { const arrayStrictEquals = function(a, b) {
var n = a.length; var n = a.length;
if ( n !== b.length ) { return false; } if ( n !== b.length ) { return false; }
var isArray, x, y; var isArray, x, y;
@ -251,22 +251,22 @@ var arrayStrictEquals = function(a, b) {
**/ **/
var filterClasses = [], const filterClasses = [];
filterClassIdGenerator = 0; let filterClassIdGenerator = 0;
var registerFilterClass = function(ctor) { const registerFilterClass = function(ctor) {
var fid = filterClassIdGenerator++; let fid = filterClassIdGenerator++;
ctor.fid = ctor.prototype.fid = fid; ctor.fid = ctor.prototype.fid = fid;
filterClasses[fid] = ctor; filterClasses[fid] = ctor;
}; };
var filterFromCompiledData = function(args) { const filterFromCompiledData = function(args) {
return filterClasses[args[0]].load(args); return filterClasses[args[0]].load(args);
}; };
/******************************************************************************/ /******************************************************************************/
var FilterTrue = function() { const FilterTrue = function() {
}; };
FilterTrue.prototype.match = function() { FilterTrue.prototype.match = function() {
@ -297,7 +297,7 @@ registerFilterClass(FilterTrue);
/******************************************************************************/ /******************************************************************************/
var FilterPlain = function(s, tokenBeg) { const FilterPlain = function(s, tokenBeg) {
this.s = s; this.s = s;
this.tokenBeg = tokenBeg; this.tokenBeg = tokenBeg;
}; };
@ -330,7 +330,7 @@ registerFilterClass(FilterPlain);
/******************************************************************************/ /******************************************************************************/
var FilterPlainPrefix0 = function(s) { const FilterPlainPrefix0 = function(s) {
this.s = s; this.s = s;
}; };
@ -362,7 +362,7 @@ registerFilterClass(FilterPlainPrefix0);
/******************************************************************************/ /******************************************************************************/
var FilterPlainPrefix1 = function(s) { const FilterPlainPrefix1 = function(s) {
this.s = s; this.s = s;
}; };
@ -394,7 +394,7 @@ registerFilterClass(FilterPlainPrefix1);
/******************************************************************************/ /******************************************************************************/
var FilterPlainHostname = function(s) { const FilterPlainHostname = function(s) {
this.s = s; this.s = s;
}; };
@ -429,7 +429,7 @@ registerFilterClass(FilterPlainHostname);
/******************************************************************************/ /******************************************************************************/
var FilterPlainLeftAnchored = function(s) { const FilterPlainLeftAnchored = function(s) {
this.s = s; this.s = s;
}; };
@ -461,7 +461,7 @@ registerFilterClass(FilterPlainLeftAnchored);
/******************************************************************************/ /******************************************************************************/
var FilterPlainRightAnchored = function(s) { const FilterPlainRightAnchored = function(s) {
this.s = s; this.s = s;
}; };
@ -493,7 +493,7 @@ registerFilterClass(FilterPlainRightAnchored);
/******************************************************************************/ /******************************************************************************/
var FilterExactMatch = function(s) { const FilterExactMatch = function(s) {
this.s = s; this.s = s;
}; };
@ -525,7 +525,7 @@ registerFilterClass(FilterExactMatch);
/******************************************************************************/ /******************************************************************************/
var FilterPlainHnAnchored = function(s) { const FilterPlainHnAnchored = function(s) {
this.s = s; this.s = s;
}; };
@ -558,7 +558,7 @@ registerFilterClass(FilterPlainHnAnchored);
/******************************************************************************/ /******************************************************************************/
var FilterGeneric = function(s, anchor) { const FilterGeneric = function(s, anchor) {
this.s = s; this.s = s;
this.anchor = anchor; this.anchor = anchor;
}; };
@ -603,7 +603,7 @@ registerFilterClass(FilterGeneric);
/******************************************************************************/ /******************************************************************************/
var FilterGenericHnAnchored = function(s) { const FilterGenericHnAnchored = function(s) {
this.s = s; this.s = s;
}; };
@ -642,7 +642,7 @@ registerFilterClass(FilterGenericHnAnchored);
/******************************************************************************/ /******************************************************************************/
var FilterGenericHnAndRightAnchored = function(s) { const FilterGenericHnAndRightAnchored = function(s) {
FilterGenericHnAnchored.call(this, s); FilterGenericHnAnchored.call(this, s);
}; };
@ -682,7 +682,7 @@ registerFilterClass(FilterGenericHnAndRightAnchored);
/******************************************************************************/ /******************************************************************************/
var FilterRegex = function(s) { const FilterRegex = function(s) {
this.re = s; this.re = s;
}; };
@ -723,7 +723,7 @@ registerFilterClass(FilterRegex);
// Filtering according to the origin. // Filtering according to the origin.
var FilterOrigin = function() { const FilterOrigin = function() {
}; };
FilterOrigin.prototype.wrapped = { FilterOrigin.prototype.wrapped = {
@ -766,7 +766,7 @@ FilterOrigin.prototype.compile = function() {
// *** start of specialized origin matchers // *** start of specialized origin matchers
var FilterOriginHit = function(domainOpt) { const FilterOriginHit = function(domainOpt) {
FilterOrigin.call(this); FilterOrigin.call(this);
this.hostname = domainOpt; this.hostname = domainOpt;
}; };
@ -792,7 +792,7 @@ FilterOriginHit.prototype = Object.create(FilterOrigin.prototype, {
// //
var FilterOriginMiss = function(domainOpt) { const FilterOriginMiss = function(domainOpt) {
FilterOrigin.call(this); FilterOrigin.call(this);
this.hostname = domainOpt.slice(1); this.hostname = domainOpt.slice(1);
}; };
@ -811,14 +811,15 @@ FilterOriginMiss.prototype = Object.create(FilterOrigin.prototype, {
var needle = this.hostname, haystack = pageHostnameRegister; var needle = this.hostname, haystack = pageHostnameRegister;
if ( haystack.endsWith(needle) === false ) { return true; } if ( haystack.endsWith(needle) === false ) { return true; }
var offset = haystack.length - needle.length; var offset = haystack.length - needle.length;
return offset !== 0 && haystack.charCodeAt(offset - 1) !== 0x2E /* '.' */; return offset !== 0 &&
haystack.charCodeAt(offset - 1) !== 0x2E /* '.' */;
} }
}, },
}); });
// //
var FilterOriginHitSet = function(domainOpt) { const FilterOriginHitSet = function(domainOpt) {
FilterOrigin.call(this); FilterOrigin.call(this);
this.domainOpt = domainOpt.length < 128 this.domainOpt = domainOpt.length < 128
? domainOpt ? domainOpt
@ -840,17 +841,17 @@ FilterOriginHitSet.prototype = Object.create(FilterOrigin.prototype, {
}, },
matchOrigin: { matchOrigin: {
value: function() { value: function() {
if ( this.oneOf === null ) { if ( hnTrieManager.isValidRef(this.oneOf) === false ) {
this.oneOf = HNTrieBuilder.fromDomainOpt(this.domainOpt); this.oneOf = hnTrieManager.fromDomainOpt(this.domainOpt);
} }
return this.oneOf.matches(pageHostnameRegister); return this.oneOf.matches(pageHostnameRegister) === 1;
} }
}, },
}); });
// //
var FilterOriginMissSet = function(domainOpt) { const FilterOriginMissSet = function(domainOpt) {
FilterOrigin.call(this); FilterOrigin.call(this);
this.domainOpt = domainOpt.length < 128 this.domainOpt = domainOpt.length < 128
? domainOpt ? domainOpt
@ -872,17 +873,19 @@ FilterOriginMissSet.prototype = Object.create(FilterOrigin.prototype, {
}, },
matchOrigin: { matchOrigin: {
value: function() { value: function() {
if ( this.noneOf === null ) { if ( hnTrieManager.isValidRef(this.noneOf) === false ) {
this.noneOf = HNTrieBuilder.fromDomainOpt(this.domainOpt.replace(/~/g, '')); this.noneOf = hnTrieManager.fromDomainOpt(
this.domainOpt.replace(/~/g, '')
);
} }
return this.noneOf.matches(pageHostnameRegister) === false; return this.noneOf.matches(pageHostnameRegister) === 0;
} }
}, },
}); });
// //
var FilterOriginMixedSet = function(domainOpt) { const FilterOriginMixedSet = function(domainOpt) {
FilterOrigin.call(this); FilterOrigin.call(this);
this.domainOpt = domainOpt.length < 128 this.domainOpt = domainOpt.length < 128
? domainOpt ? domainOpt
@ -903,20 +906,16 @@ FilterOriginMixedSet.prototype = Object.create(FilterOrigin.prototype, {
}, },
init: { init: {
value: function() { value: function() {
var oneOf = [], noneOf = [], let oneOf = [], noneOf = [];
hostnames = this.domainOpt.split('|'), for ( let hostname of this.domainOpt.split('|') ) {
i = hostnames.length,
hostname;
while ( i-- ) {
hostname = hostnames[i];
if ( hostname.charCodeAt(0) === 0x7E /* '~' */ ) { if ( hostname.charCodeAt(0) === 0x7E /* '~' */ ) {
noneOf.push(hostname.slice(1)); noneOf.push(hostname.slice(1));
} else { } else {
oneOf.push(hostname); oneOf.push(hostname);
} }
} }
this.oneOf = HNTrieBuilder.fromIterable(oneOf); this.oneOf = hnTrieManager.fromIterable(oneOf);
this.noneOf = HNTrieBuilder.fromIterable(noneOf); this.noneOf = hnTrieManager.fromIterable(noneOf);
} }
}, },
toDomainOpt: { toDomainOpt: {
@ -926,10 +925,12 @@ FilterOriginMixedSet.prototype = Object.create(FilterOrigin.prototype, {
}, },
matchOrigin: { matchOrigin: {
value: function() { value: function() {
if ( this.oneOf === null ) { this.init(); } if ( hnTrieManager.isValidRef(this.oneOf) === false ) {
var needle = pageHostnameRegister; this.init();
return this.oneOf.matches(needle) && }
this.noneOf.matches(needle) === false; let needle = pageHostnameRegister;
return this.oneOf.matches(needle) === 1 &&
this.noneOf.matches(needle) === 0;
} }
}, },
}); });
@ -981,7 +982,7 @@ registerFilterClass(FilterOrigin);
/******************************************************************************/ /******************************************************************************/
var FilterDataHolder = function(dataType, dataStr) { const FilterDataHolder = function(dataType, dataStr) {
this.dataType = dataType; this.dataType = dataType;
this.dataStr = dataStr; this.dataStr = dataStr;
this.wrapped = undefined; this.wrapped = undefined;
@ -1024,7 +1025,7 @@ registerFilterClass(FilterDataHolder);
// Helper class for storing instances of FilterDataHolder. // Helper class for storing instances of FilterDataHolder.
var FilterDataHolderEntry = function(categoryBits, tokenHash, fdata) { const FilterDataHolderEntry = function(categoryBits, tokenHash, fdata) {
this.categoryBits = categoryBits; this.categoryBits = categoryBits;
this.tokenHash = tokenHash; this.tokenHash = tokenHash;
this.filter = filterFromCompiledData(fdata); this.filter = filterFromCompiledData(fdata);
@ -1047,7 +1048,7 @@ FilterDataHolderEntry.load = function(data) {
// Dictionary of hostnames // Dictionary of hostnames
// //
var FilterHostnameDict = function() { const FilterHostnameDict = function() {
this.h = ''; // short-lived register this.h = ''; // short-lived register
this.dict = new Set(); this.dict = new Set();
}; };
@ -1138,7 +1139,7 @@ registerFilterClass(FilterHostnameDict);
/******************************************************************************/ /******************************************************************************/
var FilterPair = function(a, b) { const FilterPair = function(a, b) {
this.f1 = a; this.f1 = a;
this.f2 = b; this.f2 = b;
this.f = null; this.f = null;
@ -1217,7 +1218,7 @@ registerFilterClass(FilterPair);
/******************************************************************************/ /******************************************************************************/
var FilterBucket = function(a, b, c) { const FilterBucket = function(a, b, c) {
this.filters = []; this.filters = [];
this.f = null; this.f = null;
if ( a !== undefined ) { if ( a !== undefined ) {
@ -1315,7 +1316,7 @@ registerFilterClass(FilterBucket);
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
var FilterParser = function() { const FilterParser = function() {
this.cantWebsocket = vAPI.cantWebsocket; this.cantWebsocket = vAPI.cantWebsocket;
this.reBadDomainOptChars = /[*+?^${}()[\]\\]/; this.reBadDomainOptChars = /[*+?^${}()[\]\\]/;
this.reHostnameRule1 = /^[0-9a-z][0-9a-z.-]*[0-9a-z]$/i; this.reHostnameRule1 = /^[0-9a-z][0-9a-z.-]*[0-9a-z]$/i;
@ -1933,7 +1934,7 @@ FilterParser.prototype.makeToken = function() {
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
var FilterContainer = function() { const FilterContainer = function() {
this.reIsGeneric = /[\^\*]/; this.reIsGeneric = /[\^\*]/;
this.filterParser = new FilterParser(); this.filterParser = new FilterParser();
this.urlTokenizer = µb.urlTokenizer; this.urlTokenizer = µb.urlTokenizer;
@ -1960,6 +1961,9 @@ FilterContainer.prototype.reset = function() {
this.dataFilters = new Map(); this.dataFilters = new Map();
this.filterParser.reset(); this.filterParser.reset();
// This will invalidate all hn tries throughout uBO:
hnTrieManager.reset();
// Runtime registers // Runtime registers
this.cbRegister = undefined; this.cbRegister = undefined;
this.thRegister = undefined; this.thRegister = undefined;
@ -2052,6 +2056,15 @@ FilterContainer.prototype.freeze = function() {
/******************************************************************************/ /******************************************************************************/
// This is necessary for when the filtering engine readiness will depend
// on asynchronous operations (ex.: when loading a wasm module).
FilterContainer.prototype.readyToUse = function() {
return hnTrieManager.readyToUse();
};
/******************************************************************************/
FilterContainer.prototype.toSelfie = function() { FilterContainer.prototype.toSelfie = function() {
let categoriesToSelfie = function(categoryMap) { let categoriesToSelfie = function(categoryMap) {
let selfie = []; let selfie = [];
@ -2250,7 +2263,7 @@ FilterContainer.prototype.compileToAtomicFilter = function(
// Only static filter with an explicit type can be redirected. If we reach // Only static filter with an explicit type can be redirected. If we reach
// this point, it's because there is one or more explicit type. // this point, it's because there is one or more explicit type.
if ( parsed.badFilter === false && parsed.redirect ) { if ( parsed.redirect ) {
let redirects = µb.redirectEngine.compileRuleFromStaticFilter(parsed.raw); let redirects = µb.redirectEngine.compileRuleFromStaticFilter(parsed.raw);
if ( Array.isArray(redirects) ) { if ( Array.isArray(redirects) ) {
for ( let redirect of redirects ) { for ( let redirect of redirects ) {
@ -2292,26 +2305,24 @@ FilterContainer.prototype.fromCompiledContent = function(reader) {
FilterContainer.prototype.matchAndFetchData = function(dataType, requestURL, out, outlog) { FilterContainer.prototype.matchAndFetchData = function(dataType, requestURL, out, outlog) {
if ( this.dataFilters.length === 0 ) { return; } if ( this.dataFilters.length === 0 ) { return; }
var url = this.urlTokenizer.setURL(requestURL); let url = this.urlTokenizer.setURL(requestURL);
requestHostnameRegister = µb.URI.hostnameFromURI(url); pageHostnameRegister = requestHostnameRegister = µb.URI.hostnameFromURI(url);
// We need to visit ALL the matching filters. // We need to visit ALL the matching filters.
var toAddImportant = new Map(), let toAddImportant = new Map(),
toAdd = new Map(), toAdd = new Map(),
toRemove = new Map(); toRemove = new Map();
var entry, f, let tokenHashes = this.urlTokenizer.getTokens(),
tokenHashes = this.urlTokenizer.getTokens(),
tokenHash, tokenOffset,
i = 0; i = 0;
while ( i < 32 ) { while ( i < 32 ) {
tokenHash = tokenHashes[i++]; let tokenHash = tokenHashes[i++];
if ( tokenHash === 0 ) { break; } if ( tokenHash === 0 ) { break; }
tokenOffset = tokenHashes[i++]; let tokenOffset = tokenHashes[i++];
entry = this.dataFilters.get(tokenHash); let entry = this.dataFilters.get(tokenHash);
while ( entry !== undefined ) { while ( entry !== undefined ) {
f = entry.filter; let f = entry.filter;
if ( f.match(url, tokenOffset) === true ) { if ( f.match(url, tokenOffset) === true ) {
if ( entry.categoryBits & 0x001 ) { if ( entry.categoryBits & 0x001 ) {
toRemove.set(f.dataStr, entry); toRemove.set(f.dataStr, entry);
@ -2324,9 +2335,9 @@ FilterContainer.prototype.matchAndFetchData = function(dataType, requestURL, out
entry = entry.next; entry = entry.next;
} }
} }
entry = this.dataFilters.get(this.noTokenHash); let entry = this.dataFilters.get(this.noTokenHash);
while ( entry !== undefined ) { while ( entry !== undefined ) {
f = entry.filter; let f = entry.filter;
if ( f.match(url) === true ) { if ( f.match(url) === true ) {
if ( entry.categoryBits & 0x001 ) { if ( entry.categoryBits & 0x001 ) {
toRemove.set(f.dataStr, entry); toRemove.set(f.dataStr, entry);
@ -2342,12 +2353,11 @@ FilterContainer.prototype.matchAndFetchData = function(dataType, requestURL, out
if ( toAddImportant.size === 0 && toAdd.size === 0 ) { return; } if ( toAddImportant.size === 0 && toAdd.size === 0 ) { return; }
// Remove entries overriden by other filters. // Remove entries overriden by other filters.
var key; for ( let key of toAddImportant.keys() ) {
for ( key of toAddImportant.keys() ) {
toAdd.delete(key); toAdd.delete(key);
toRemove.delete(key); toRemove.delete(key);
} }
for ( key of toRemove.keys() ) { for ( let key of toRemove.keys() ) {
if ( key === '' ) { if ( key === '' ) {
toAdd.clear(); toAdd.clear();
break; break;
@ -2355,26 +2365,25 @@ FilterContainer.prototype.matchAndFetchData = function(dataType, requestURL, out
toAdd.delete(key); toAdd.delete(key);
} }
var logData; for ( let entry of toAddImportant ) {
for ( entry of toAddImportant ) {
out.push(entry[0]); out.push(entry[0]);
if ( outlog === undefined ) { continue; } if ( outlog === undefined ) { continue; }
logData = entry[1].logData(); let logData = entry[1].logData();
logData.source = 'static'; logData.source = 'static';
logData.result = 1; logData.result = 1;
outlog.push(logData); outlog.push(logData);
} }
for ( entry of toAdd ) { for ( let entry of toAdd ) {
out.push(entry[0]); out.push(entry[0]);
if ( outlog === undefined ) { continue; } if ( outlog === undefined ) { continue; }
logData = entry[1].logData(); let logData = entry[1].logData();
logData.source = 'static'; logData.source = 'static';
logData.result = 1; logData.result = 1;
outlog.push(logData); outlog.push(logData);
} }
if ( outlog !== undefined ) { if ( outlog !== undefined ) {
for ( entry of toRemove.values()) { for ( let entry of toRemove.values()) {
logData = entry.logData(); let logData = entry.logData();
logData.source = 'static'; logData.source = 'static';
logData.result = 2; logData.result = 2;
outlog.push(logData); outlog.push(logData);
@ -2389,20 +2398,19 @@ FilterContainer.prototype.matchAndFetchData = function(dataType, requestURL, out
FilterContainer.prototype.matchTokens = function(bucket, url) { FilterContainer.prototype.matchTokens = function(bucket, url) {
// Hostname-only filters // Hostname-only filters
var f = bucket.get(this.dotTokenHash); let f = bucket.get(this.dotTokenHash);
if ( f !== undefined && f.match() === true ) { if ( f !== undefined && f.match() === true ) {
this.thRegister = this.dotTokenHash; this.thRegister = this.dotTokenHash;
this.fRegister = f; this.fRegister = f;
return true; return true;
} }
var tokenHashes = this.urlTokenizer.getTokens(), let tokenHashes = this.urlTokenizer.getTokens(),
tokenHash, tokenOffset,
i = 0; i = 0;
for (;;) { for (;;) {
tokenHash = tokenHashes[i++]; let tokenHash = tokenHashes[i++];
if ( tokenHash === 0 ) { break; } if ( tokenHash === 0 ) { break; }
tokenOffset = tokenHashes[i++]; let tokenOffset = tokenHashes[i++];
f = bucket.get(tokenHash); f = bucket.get(tokenHash);
if ( f !== undefined && f.match(url, tokenOffset) === true ) { if ( f !== undefined && f.match(url, tokenOffset) === true ) {
this.thRegister = tokenHash; this.thRegister = tokenHash;
@ -2437,8 +2445,10 @@ FilterContainer.prototype.matchStringGenericHide = function(requestURL) {
let url = this.urlTokenizer.setURL(requestURL); let url = this.urlTokenizer.setURL(requestURL);
// https://github.com/gorhill/uBlock/issues/2225 // https://github.com/gorhill/uBlock/issues/2225
// Important: this is used by FilterHostnameDict.match(). // Important:
requestHostnameRegister = µb.URI.hostnameFromURI(url); // - `pageHostnameRegister` is used by FilterOrigin.matchOrigin().
// - `requestHostnameRegister` is used by FilterHostnameDict.match().
pageHostnameRegister = requestHostnameRegister = µb.URI.hostnameFromURI(url);
let bucket = this.categories.get(genericHideException); let bucket = this.categories.get(genericHideException);
if ( !bucket || this.matchTokens(bucket, url) === false ) { if ( !bucket || this.matchTokens(bucket, url) === false ) {
@ -2548,7 +2558,7 @@ FilterContainer.prototype.matchString = function(context) {
// https://github.com/chrisaljoudi/uBlock/issues/519 // https://github.com/chrisaljoudi/uBlock/issues/519
// Use exact type match for anything beyond `other` // Use exact type match for anything beyond `other`
// Also, be prepared to support unknown types // Also, be prepared to support unknown types
var type = typeNameToTypeValue[context.requestType]; let type = typeNameToTypeValue[context.requestType];
if ( type === undefined ) { if ( type === undefined ) {
type = otherTypeBitValue; type = otherTypeBitValue;
} else if ( type === 0 || type > otherTypeBitValue ) { } else if ( type === 0 || type > otherTypeBitValue ) {
@ -2577,7 +2587,7 @@ FilterContainer.prototype.matchString = function(context) {
// filter. // filter.
// Prime tokenizer: we get a normalized URL in return. // Prime tokenizer: we get a normalized URL in return.
var url = this.urlTokenizer.setURL(context.requestURL); let url = this.urlTokenizer.setURL(context.requestURL);
// These registers will be used by various filters // These registers will be used by various filters
pageHostnameRegister = context.pageHostname || ''; pageHostnameRegister = context.pageHostname || '';
@ -2585,10 +2595,10 @@ FilterContainer.prototype.matchString = function(context) {
this.fRegister = null; this.fRegister = null;
var party = isFirstParty(context.pageDomain, context.requestHostname) let party = isFirstParty(context.pageDomain, context.requestHostname)
? FirstParty ? FirstParty
: ThirdParty; : ThirdParty;
var categories = this.categories, let categories = this.categories,
catBits, bucket; catBits, bucket;
// https://github.com/chrisaljoudi/uBlock/issues/139 // https://github.com/chrisaljoudi/uBlock/issues/139

View File

@ -604,9 +604,7 @@
µBlock.loadFilterLists = function(callback) { µBlock.loadFilterLists = function(callback) {
// Callers are expected to check this first. // Callers are expected to check this first.
if ( this.loadingFilterLists ) { if ( this.loadingFilterLists ) { return; }
return;
}
this.loadingFilterLists = true; this.loadingFilterLists = true;
var µb = this, var µb = this,
@ -961,38 +959,31 @@
/******************************************************************************/ /******************************************************************************/
µBlock.loadPublicSuffixList = function(callback) { µBlock.loadPublicSuffixList = function() {
var µb = this, return new Promise(resolve => {
assetKey = µb.pslAssetKey, // start of executor
compiledAssetKey = 'compiled/' + assetKey; this.assets.get('compiled/' + this.pslAssetKey, details => {
let selfie;
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
}
var onRawListLoaded = function(details) {
if ( details.content !== '' ) {
µb.compilePublicSuffixList(details.content);
}
callback();
};
var onCompiledListLoaded = function(details) {
var selfie;
try { try {
selfie = JSON.parse(details.content); selfie = JSON.parse(details.content);
} catch (ex) { } catch (ex) {
} }
if ( if (
selfie === undefined || selfie instanceof Object &&
publicSuffixList.fromSelfie(selfie) === false publicSuffixList.fromSelfie(selfie)
) { ) {
µb.assets.get(assetKey, onRawListLoaded); resolve();
return; return;
} }
callback(); this.assets.get(this.pslAssetKey, details => {
}; if ( details.content !== '' ) {
this.compilePublicSuffixList(details.content);
this.assets.get(compiledAssetKey, onCompiledListLoaded); }
resolve();
});
});
// end of executor
});
}; };
/******************************************************************************/ /******************************************************************************/

24
src/js/wasm/README.md Normal file
View File

@ -0,0 +1,24 @@
### For code reviewers
All `wasm` files in that directory where created by compiling the
corresponding `wat` file using the command (using `hntrie.wat`/`hntrie.wasm`
as example):
wat2wasm hntrie.wat -o hntrie.wasm
Assuming:
- The command is executed from within the present directory.
### `wat2wasm` tool
The `wat2wasm` tool can be downloaded from an official WebAssembly project:
<https://github.com/WebAssembly/wabt/releases>.
### `wat2wasm` tool online
You can also use the following online `wat2wasm` tool:
<https://webassembly.github.io/wabt/demo/wat2wasm/>.
Just paste the whole content of the `wat` file to compile into the WAT pane.
Click "Download" button to retrieve the resulting `wasm` file.

BIN
src/js/wasm/hntrie.wasm Normal file

Binary file not shown.

200
src/js/wasm/hntrie.wat Normal file
View File

@ -0,0 +1,200 @@
;;
;; uBlock Origin - a browser extension to block requests.
;; Copyright (C) 2018-present 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
;; File: hntrie.wat
;; Description: WebAssembly code used by src/js/hntrie.js
;; How to compile: See README.md in this directory.
(module
;;
;; module start
;;
;; (func $log (import "imports" "log") (param i32 i32 i32))
(memory (import "imports" "memory") 1)
;;
;; Public functions
;;
;;
;; unsigned int matches(offset)
;;
;; Test whether the currently set needle matches the trie at specified offset.
;;
;; Memory layout, byte offset:
;; 0-254: encoded needle (ASCII)
;; 255 : needle length
;; 256- : tries
;;
(func (export "matches")
(param $itrie i32)
(result i32) ;; result: 0 = miss, 1 = hit
(local $ineedle i32) ;; current needle offset
(local $nchar i32) ;; needle char being processed
(local $tchar i32) ;; trie char being processed
(local $lxtra i32)
(local $ixtra i32)
i32.const 255
i32.load8_u
set_local $ineedle
loop $nextNeedleChar
;; ineedle -= 1;
get_local $ineedle
i32.const -1
i32.add
tee_local $ineedle
;; let nchar = ineedle === -1 ? 0 : buf[ineedle];
i32.const 0
i32.lt_s
if
i32.const 0
set_local $nchar
else
get_local $ineedle
i32.load8_u
set_local $nchar
end
block $trieCharEqNeedleChar loop $nextTrieChar
;; let tchar = buf[itrie+8];
get_local $itrie
i32.load8_u offset=8
tee_local $tchar
;; if ( tchar === nchar ) { break; }
get_local $nchar
i32.eq
br_if $trieCharEqNeedleChar
;; if ( tchar === 0 && nchar === 0x2E ) { return 1; }
get_local $tchar
i32.eqz
if
get_local $nchar
i32.const 0x2E
i32.eq
if
i32.const 1
return
end
end
;; itrie = buf32[itrie >>> 2];
get_local $itrie
i32.load
tee_local $itrie
;; if ( itrie === 0 ) { return 0; }
i32.eqz
if
i32.const 0
return
end
br $nextTrieChar
end end
;; if ( nchar === 0 ) { return 1; }
get_local $nchar
i32.eqz
if
i32.const 1
return
end
;; let lxtra = buf[itrie+9];
get_local $itrie
i32.load8_u offset=9
tee_local $lxtra
i32.eqz
if else
;; if ( lxtra > ineedle ) { return 0; }
get_local $lxtra
get_local $ineedle
i32.gt_u
if
i32.const 0
return
end
;; let ixtra = itrie + 10;
get_local $itrie
i32.const 10
i32.add
tee_local $ixtra
;; lxtra += ixtra;
get_local $lxtra
i32.add
set_local $lxtra
;; do {
block $noMoreExtraChars loop
;; ineedle -= 1;
get_local $ineedle
i32.const -1
i32.add
tee_local $ineedle
;; if ( buf[ineedle] !== buf[ixtra] ) { return 0; }
i32.load8_u
get_local $ixtra
i32.load8_u
i32.ne
if
i32.const 0
return
end
;; ixtra += 1;
get_local $ixtra
i32.const 1
i32.add
tee_local $ixtra
;; while ( ixtra !== lxtra ) {
get_local $lxtra
i32.eq
br_if $noMoreExtraChars
br 0
end end
end
;; itrie = buf32[itrie + 4 >>> 2];
get_local $itrie
i32.load offset=4
tee_local $itrie
;; if ( itrie === 0 ) {
i32.eqz
if
;; return ineedle === 0 || buf[ineedle-1] === 0x2E ? 1 : 0;
get_local $ineedle
i32.eqz
if
i32.const 1
return
end
get_local $ineedle
i32.const -1
i32.add
i32.load8_u
i32.const 0x2E
i32.eq
if
i32.const 1
return
end
i32.const 0
return
end
br 0
end
i32.const 0
)
;;
;; module end
;;
)

479
test/hnset-benchmark.html Normal file

File diff suppressed because one or more lines are too long

45866
test/hntrie-test.html Normal file

File diff suppressed because it is too large Load Diff