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,
oneMinute = 60 * oneSecond;
const oneSecond = 1000,
oneMinute = 60 * oneSecond;
var hiddenSettingsDefault = {
const hiddenSettingsDefault = {
assetFetchTimeout: 30,
autoUpdateAssetFetchPeriod: 120,
autoUpdatePeriod: 7,
@ -56,7 +56,7 @@ var µBlock = (function() { // jshint ignore:line
userResourcesLocation: 'unset'
};
var whitelistDefault = [
const whitelistDefault = [
'about-scheme',
'chrome-extension-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);
onCommandShortcutsReady(fetched.commandShortcuts);
µb.loadPublicSuffixList(onPSLReady);
Promise.all([
µb.loadPublicSuffixList(),
µb.staticNetFilteringEngine.readyToUse()
]).then(( ) => {
onPSLReady();
});
µb.loadRedirectResources();
};

View File

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

View File

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