1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-05 18:49:39 +02:00

this mitigates #520, #376

This commit is contained in:
gorhill 2015-02-23 18:31:29 -05:00
parent ae15aca35f
commit c7bab5502e
12 changed files with 977 additions and 579 deletions

View File

@ -2,7 +2,7 @@
"manifest_version": 2, "manifest_version": 2,
"name": "µBlock", "name": "µBlock",
"version": "0.8.8.4", "version": "0.8.8.5",
"default_locale": "en", "default_locale": "en",
"description": "__MSG_extShortDesc__", "description": "__MSG_extShortDesc__",

View File

@ -43,7 +43,7 @@ var re3rdPartyExternalAsset = /^https?:\/\/[a-z0-9]+/;
var onMessage = function(msg) { var onMessage = function(msg) {
switch ( msg.what ) { switch ( msg.what ) {
case 'loadUbiquitousBlacklistCompleted': case 'allFilterListsReloaded':
renderBlacklists(); renderBlacklists();
break; break;

View File

@ -57,6 +57,7 @@ var thirdpartiesRepositoryRoot = 'https://raw.githubusercontent.com/gorhill/uAss
var nullFunc = function() {}; var nullFunc = function() {};
var reIsExternalPath = /^[a-z]+:\/\//; var reIsExternalPath = /^[a-z]+:\/\//;
var reIsUserPath = /^assets\/user\//; var reIsUserPath = /^assets\/user\//;
var reIsCachePath = /^cache:\/\//;
var lastRepoMetaTimestamp = 0; var lastRepoMetaTimestamp = 0;
var lastRepoMetaIsRemote = false; var lastRepoMetaIsRemote = false;
var refreshRepoMetaPeriod = 5 * oneHour; var refreshRepoMetaPeriod = 5 * oneHour;
@ -197,17 +198,26 @@ var cachedAssetsManager = (function() {
var cachedContentPath = cachedAssetPathPrefix + path; var cachedContentPath = cachedAssetPathPrefix + path;
var bin = {}; var bin = {};
bin[cachedContentPath] = content; bin[cachedContentPath] = content;
var removedItems = [];
var onSaved = function() { var onSaved = function() {
var lastError = vAPI.lastError(); var lastError = vAPI.lastError();
if ( lastError ) { if ( lastError ) {
details.error = 'Error: ' + lastError.message; details.error = 'Error: ' + lastError.message;
console.error('µBlock> cachedAssetsManager.save():', details.error); console.error('µBlock> cachedAssetsManager.save():', details.error);
cbError(details); cbError(details);
} else { return;
cbSuccess(details);
} }
// Saving over an existing item must be seen as removing an
// existing item and adding a new one.
if ( typeof exports.onRemovedListener === 'function' ) {
exports.onRemovedListener(removedItems);
}
cbSuccess(details);
}; };
var onEntries = function(entries) { var onEntries = function(entries) {
if ( entries.hasOwnProperty(path) ) {
removedItems.push(path);
}
entries[path] = Date.now(); entries[path] = Date.now();
bin.cached_asset_entries = entries; bin.cached_asset_entries = entries;
vAPI.storage.set(bin, onSaved); vAPI.storage.set(bin, onSaved);
@ -218,6 +228,7 @@ var cachedAssetsManager = (function() {
exports.remove = function(pattern, before) { exports.remove = function(pattern, before) {
var onEntries = function(entries) { var onEntries = function(entries) {
var keystoRemove = []; var keystoRemove = [];
var removedItems = [];
var paths = Object.keys(entries); var paths = Object.keys(entries);
var i = paths.length; var i = paths.length;
var path; var path;
@ -232,12 +243,16 @@ var cachedAssetsManager = (function() {
if ( typeof before === 'number' && entries[path] >= before ) { if ( typeof before === 'number' && entries[path] >= before ) {
continue; continue;
} }
removedItems.push(path);
keystoRemove.push(cachedAssetPathPrefix + path); keystoRemove.push(cachedAssetPathPrefix + path);
delete entries[path]; delete entries[path];
} }
if ( keystoRemove.length ) { if ( keystoRemove.length ) {
vAPI.storage.remove(keystoRemove); vAPI.storage.remove(keystoRemove);
vAPI.storage.set({ 'cached_asset_entries': entries }); vAPI.storage.set({ 'cached_asset_entries': entries });
if ( typeof exports.onRemovedListener === 'function' ) {
exports.onRemovedListener(removedItems);
}
} }
}; };
getEntries(onEntries); getEntries(onEntries);
@ -245,8 +260,10 @@ var cachedAssetsManager = (function() {
exports.removeAll = function(callback) { exports.removeAll = function(callback) {
var onEntries = function() { var onEntries = function() {
// Careful! do not remove 'assets/user/'
exports.remove(/^https?:\/\/[a-z0-9]+/); exports.remove(/^https?:\/\/[a-z0-9]+/);
exports.remove(/^assets\/(ublock|thirdparties)\//); exports.remove(/^assets\/(ublock|thirdparties)\//);
exports.remove(/^cache:\/\//);
exports.remove('assets/checksums.txt'); exports.remove('assets/checksums.txt');
if ( typeof callback === 'function' ) { if ( typeof callback === 'function' ) {
callback(null); callback(null);
@ -255,6 +272,8 @@ var cachedAssetsManager = (function() {
getEntries(onEntries); getEntries(onEntries);
}; };
exports.onRemovedListener = null;
return exports; return exports;
})(); })();
@ -263,15 +282,18 @@ var cachedAssetsManager = (function() {
var getTextFileFromURL = function(url, onLoad, onError) { var getTextFileFromURL = function(url, onLoad, onError) {
// https://github.com/gorhill/uMatrix/issues/15 // https://github.com/gorhill/uMatrix/issues/15
var onResponseReceived = function() { var onResponseReceived = function() {
if ( this.status !== 0 && ( this.status < 200 || this.status >= 300 ) ) { this.onload = this.onerror = this.ontimeout = null;
// xhr for local files gives status 0, but actually succeeds
var status = this.status || 200;
if ( status < 200 || status >= 300 ) {
return onError.call(this); return onError.call(this);
} }
// xhr for local files gives status 0, but actually succeeds // consider an empty result to be an error
if ( this.status === 0 && stringIsNotEmpty(this.responseText) === false ) { if ( stringIsNotEmpty(this.responseText) === false ) {
return onError.call(this); return onError.call(this);
} }
// we never download anything else than plain text: discard if response // we never download anything else than plain text: discard if response
// appears to be a HTML document: could happen when server returns // appears to be a HTML document: could happen when server serves
// some kind of error page I suppose // some kind of error page I suppose
var text = this.responseText.trim(); var text = this.responseText.trim();
if ( text.charAt(0) === '<' && text.slice(-1) === '>' ) { if ( text.charAt(0) === '<' && text.slice(-1) === '>' ) {
@ -279,15 +301,23 @@ var getTextFileFromURL = function(url, onLoad, onError) {
} }
return onLoad.call(this); return onLoad.call(this);
}; };
var onErrorReceived = function() {
this.onload = this.onerror = this.ontimeout = null;
onError.call(this);
};
// console.log('µBlock> getTextFileFromURL("%s"):', url); // console.log('µBlock> getTextFileFromURL("%s"):', url);
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
try {
xhr.open('get', url, true); xhr.open('get', url, true);
xhr.timeout = 30000; xhr.timeout = 30000;
xhr.onload = onResponseReceived; xhr.onload = onResponseReceived;
xhr.onerror = onError; xhr.onerror = onErrorReceived;
xhr.ontimeout = onError; xhr.ontimeout = onErrorReceived;
xhr.responseType = 'text'; xhr.responseType = 'text';
xhr.send(); xhr.send();
} catch (e) {
onErrorReceived.call(xhr);
}
}; };
/******************************************************************************/ /******************************************************************************/
@ -454,13 +484,11 @@ var readLocalFile = function(path, callback) {
}; };
var onInstallFileLoaded = function() { var onInstallFileLoaded = function() {
this.onload = this.onerror = null;
//console.log('µBlock> readLocalFile("%s") / onInstallFileLoaded()', path); //console.log('µBlock> readLocalFile("%s") / onInstallFileLoaded()', path);
reportBack(this.responseText); reportBack(this.responseText);
}; };
var onInstallFileError = function() { var onInstallFileError = function() {
this.onload = this.onerror = null;
console.error('µBlock> readLocalFile("%s") / onInstallFileError()', path); console.error('µBlock> readLocalFile("%s") / onInstallFileError()', path);
reportBack('', 'Error'); reportBack('', 'Error');
}; };
@ -512,7 +540,6 @@ var readRepoFile = function(path, callback) {
var repositoryURL = projectRepositoryRoot + path; var repositoryURL = projectRepositoryRoot + path;
var onRepoFileLoaded = function() { var onRepoFileLoaded = function() {
this.onload = this.onerror = null;
//console.log('µBlock> readRepoFile("%s") / onRepoFileLoaded()', path); //console.log('µBlock> readRepoFile("%s") / onRepoFileLoaded()', path);
// https://github.com/gorhill/httpswitchboard/issues/263 // https://github.com/gorhill/httpswitchboard/issues/263
if ( this.status === 200 ) { if ( this.status === 200 ) {
@ -523,7 +550,6 @@ var readRepoFile = function(path, callback) {
}; };
var onRepoFileError = function() { var onRepoFileError = function() {
this.onload = this.onerror = null;
console.error(errorCantConnectTo.replace('{{url}}', repositoryURL)); console.error(errorCantConnectTo.replace('{{url}}', repositoryURL));
reportBack('', 'Error'); reportBack('', 'Error');
}; };
@ -568,13 +594,11 @@ var readRepoCopyAsset = function(path, callback) {
}; };
var onInstallFileLoaded = function() { var onInstallFileLoaded = function() {
this.onload = this.onerror = null;
//console.log('µBlock> readRepoCopyAsset("%s") / onInstallFileLoaded()', path); //console.log('µBlock> readRepoCopyAsset("%s") / onInstallFileLoaded()', path);
reportBack(this.responseText); reportBack(this.responseText);
}; };
var onInstallFileError = function() { var onInstallFileError = function() {
this.onload = this.onerror = null;
console.error('µBlock> readRepoCopyAsset("%s") / onInstallFileError():', path, this.statusText); console.error('µBlock> readRepoCopyAsset("%s") / onInstallFileError():', path, this.statusText);
reportBack('', 'Error'); reportBack('', 'Error');
}; };
@ -593,7 +617,6 @@ var readRepoCopyAsset = function(path, callback) {
var repositoryURLSkipCache = repositoryURL + '?ublock=' + Date.now(); var repositoryURLSkipCache = repositoryURL + '?ublock=' + Date.now();
var onRepoFileLoaded = function() { var onRepoFileLoaded = function() {
this.onload = this.onerror = null;
if ( stringIsNotEmpty(this.responseText) === false ) { if ( stringIsNotEmpty(this.responseText) === false ) {
console.error('µBlock> readRepoCopyAsset("%s") / onRepoFileLoaded("%s"): error', path, repositoryURL); console.error('µBlock> readRepoCopyAsset("%s") / onRepoFileLoaded("%s"): error', path, repositoryURL);
cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError);
@ -605,13 +628,11 @@ var readRepoCopyAsset = function(path, callback) {
}; };
var onRepoFileError = function() { var onRepoFileError = function() {
this.onload = this.onerror = null;
console.error(errorCantConnectTo.replace('{{url}}', repositoryURL)); console.error(errorCantConnectTo.replace('{{url}}', repositoryURL));
cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError);
}; };
var onHomeFileLoaded = function() { var onHomeFileLoaded = function() {
this.onload = this.onerror = null;
if ( stringIsNotEmpty(this.responseText) === false ) { if ( stringIsNotEmpty(this.responseText) === false ) {
console.error('µBlock> readRepoCopyAsset("%s") / onHomeFileLoaded("%s"): no response', path, homeURL); console.error('µBlock> readRepoCopyAsset("%s") / onHomeFileLoaded("%s"): no response', path, homeURL);
// Fetch from repo only if obsolescence was due to repo checksum // Fetch from repo only if obsolescence was due to repo checksum
@ -628,7 +649,6 @@ var readRepoCopyAsset = function(path, callback) {
}; };
var onHomeFileError = function() { var onHomeFileError = function() {
this.onload = this.onerror = null;
console.error(errorCantConnectTo.replace('{{url}}', homeURL)); console.error(errorCantConnectTo.replace('{{url}}', homeURL));
// Fetch from repo only if obsolescence was due to repo checksum // Fetch from repo only if obsolescence was due to repo checksum
if ( assetEntry.localChecksum !== assetEntry.repoChecksum ) { if ( assetEntry.localChecksum !== assetEntry.repoChecksum ) {
@ -715,13 +735,11 @@ var readRepoOnlyAsset = function(path, callback) {
}; };
var onInstallFileLoaded = function() { var onInstallFileLoaded = function() {
this.onload = this.onerror = null;
//console.log('µBlock> readRepoOnlyAsset("%s") / onInstallFileLoaded()', path); //console.log('µBlock> readRepoOnlyAsset("%s") / onInstallFileLoaded()', path);
reportBack(this.responseText); reportBack(this.responseText);
}; };
var onInstallFileError = function() { var onInstallFileError = function() {
this.onload = this.onerror = null;
console.error('µBlock> readRepoOnlyAsset("%s") / onInstallFileError()', path); console.error('µBlock> readRepoOnlyAsset("%s") / onInstallFileError()', path);
reportBack('', 'Error'); reportBack('', 'Error');
}; };
@ -739,7 +757,6 @@ var readRepoOnlyAsset = function(path, callback) {
var repositoryURL = projectRepositoryRoot + path + '?ublock=' + Date.now(); var repositoryURL = projectRepositoryRoot + path + '?ublock=' + Date.now();
var onRepoFileLoaded = function() { var onRepoFileLoaded = function() {
this.onload = this.onerror = null;
if ( typeof this.responseText !== 'string' ) { if ( typeof this.responseText !== 'string' ) {
console.error('µBlock> readRepoOnlyAsset("%s") / onRepoFileLoaded("%s"): no response', path, repositoryURL); console.error('µBlock> readRepoOnlyAsset("%s") / onRepoFileLoaded("%s"): no response', path, repositoryURL);
cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError);
@ -757,7 +774,6 @@ var readRepoOnlyAsset = function(path, callback) {
}; };
var onRepoFileError = function() { var onRepoFileError = function() {
this.onload = this.onerror = null;
console.error(errorCantConnectTo.replace('{{url}}', repositoryURL)); console.error(errorCantConnectTo.replace('{{url}}', repositoryURL));
cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError);
}; };
@ -827,7 +843,6 @@ var readExternalAsset = function(path, callback) {
}; };
var onExternalFileLoaded = function() { var onExternalFileLoaded = function() {
this.onload = this.onerror = null;
// https://github.com/gorhill/uBlock/issues/708 // https://github.com/gorhill/uBlock/issues/708
// A successful download should never return an empty file: turn this // A successful download should never return an empty file: turn this
// into an error condition. // into an error condition.
@ -841,7 +856,6 @@ var readExternalAsset = function(path, callback) {
}; };
var onExternalFileError = function() { var onExternalFileError = function() {
this.onload = this.onerror = null;
console.error(errorCantConnectTo.replace('{{url}}', path)); console.error(errorCantConnectTo.replace('{{url}}', path));
cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError);
}; };
@ -874,12 +888,33 @@ var readExternalAsset = function(path, callback) {
var readUserAsset = function(path, callback) { var readUserAsset = function(path, callback) {
var onCachedContentLoaded = function(details) { var onCachedContentLoaded = function(details) {
//console.log('µBlock> readUserAsset("%s") / onCachedContentLoaded()', path); //console.log('µBlock.assets/readUserAsset("%s")/onCachedContentLoaded()', path);
callback({ 'path': path, 'content': details.content }); callback({ 'path': path, 'content': details.content });
}; };
var onCachedContentError = function() { var onCachedContentError = function() {
//console.log('µBlock> readUserAsset("%s") / onCachedContentError()', path); //console.log('µBlock.assets/readUserAsset("%s")/onCachedContentError()', path);
callback({ 'path': path, 'content': '' });
};
cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError);
};
/******************************************************************************/
// Asset available only from the cache.
// Cache data:
// Path --> starts with 'cache://'
// Cache --> whatever
var readCacheAsset = function(path, callback) {
var onCachedContentLoaded = function(details) {
//console.log('µBlock.assets/readCacheAsset("%s")/onCachedContentLoaded()', path);
callback({ 'path': path, 'content': details.content });
};
var onCachedContentError = function() {
//console.log('µBlock.assets/readCacheAsset("%s")/onCachedContentError()', path);
callback({ 'path': path, 'content': '' }); callback({ 'path': path, 'content': '' });
}; };
@ -930,6 +965,11 @@ exports.get = function(path, callback) {
return; return;
} }
if ( reIsCachePath.test(path) ) {
readCacheAsset(path, callback);
return;
}
if ( reIsExternalPath.test(path) ) { if ( reIsExternalPath.test(path) ) {
readExternalAsset(path, callback); readExternalAsset(path, callback);
return; return;
@ -1042,14 +1082,20 @@ exports.purge = function(pattern, before) {
cachedAssetsManager.remove(pattern, before); cachedAssetsManager.remove(pattern, before);
}; };
/******************************************************************************/
exports.purgeAll = function(callback) { exports.purgeAll = function(callback) {
cachedAssetsManager.removeAll(callback); cachedAssetsManager.removeAll(callback);
}; };
/******************************************************************************/ /******************************************************************************/
exports.onAssetCacheRemoved = {
addEventListener: function(callback) {
cachedAssetsManager.onRemovedListener = callback || null;
}
};
/******************************************************************************/
return exports; return exports;
})(); })();
@ -1076,8 +1122,9 @@ var updated = {};
var updatedCount = 0; var updatedCount = 0;
var metadata = null; var metadata = null;
var onStart = null; var onStartListener = null;
var onCompleted = null; var onCompletedListener = null;
var onAssetUpdatedListener = null;
var exports = {}; var exports = {};
@ -1086,15 +1133,15 @@ var exports = {};
var onAssetUpdated = function(details) { var onAssetUpdated = function(details) {
var path = details.path; var path = details.path;
if ( details.error ) { if ( details.error ) {
//console.debug('assets.js > µBlock.assetUpdater/onAssetUpdated: "%s" failed', path); //console.debug('µBlock.assetUpdater/onAssetUpdated: "%s" failed', path);
return; return;
} }
//console.debug('assets.js > µBlock.assetUpdater/onAssetUpdated: "%s"', path); //console.debug('µBlock.assetUpdater/onAssetUpdated: "%s"', path);
updated[path] = true; updated[path] = true;
updatedCount += 1; updatedCount += 1;
if ( typeof onAssetUpdatedListener === 'function' ) {
// New data available: selfie is now invalid onAssetUpdatedListener(details);
µb.destroySelfie(); }
}; };
/******************************************************************************/ /******************************************************************************/
@ -1117,7 +1164,7 @@ var updateOne = function() {
if ( !metaEntry.cacheObsolete && !metaEntry.repoObsolete ) { if ( !metaEntry.cacheObsolete && !metaEntry.repoObsolete ) {
continue; continue;
} }
//console.debug('assets.js > µBlock.assetUpdater/updateOne: assets.get("%s")', path); //console.debug('µBlock.assetUpdater/updateOne: assets.get("%s")', path);
µb.assets.get(path, onAssetUpdated); µb.assets.get(path, onAssetUpdated);
break; break;
} }
@ -1144,10 +1191,10 @@ var updateDaemon = function() {
// Start an update cycle? // Start an update cycle?
if ( updateCycleTime !== 0 ) { if ( updateCycleTime !== 0 ) {
if ( Date.now() >= updateCycleTime ) { if ( Date.now() >= updateCycleTime ) {
//console.debug('assets.js > µBlock.assetUpdater/updateDaemon: update cycle started'); //console.debug('µBlock.assetUpdater/updateDaemon: update cycle started');
reset(); reset();
if ( onStart !== null ) { if ( typeof onStartListener === 'function' ) {
onStart(); onStartListener();
} }
} }
return; return;
@ -1166,9 +1213,9 @@ var updateDaemon = function() {
// If anything was updated, notify listener // If anything was updated, notify listener
if ( updatedCount !== 0 ) { if ( updatedCount !== 0 ) {
if ( onCompleted !== null ) { if ( typeof onCompletedListener === 'function' ) {
//console.debug('assets.js > µBlock.assetUpdater/updateDaemon: update cycle completed'); //console.debug('µBlock.assetUpdater/updateDaemon: update cycle completed');
onCompleted({ onCompletedListener({
updated: JSON.parse(JSON.stringify(updated)), // give callee its own safe copy updated: JSON.parse(JSON.stringify(updated)), // give callee its own safe copy
updatedCount: updatedCount updatedCount: updatedCount
}); });
@ -1178,7 +1225,7 @@ var updateDaemon = function() {
// Schedule next update cycle // Schedule next update cycle
if ( updateCycleTime === 0 ) { if ( updateCycleTime === 0 ) {
reset(); reset();
//console.debug('assets.js > µBlock.assetUpdater/updateDaemon: update cycle re-scheduled'); //console.debug('µBlock.assetUpdater/updateDaemon: update cycle re-scheduled');
updateCycleTime = Date.now() + updateCycleNextPeriod; updateCycleTime = Date.now() + updateCycleNextPeriod;
} }
}; };
@ -1200,8 +1247,8 @@ var reset = function() {
exports.onStart = { exports.onStart = {
addEventListener: function(callback) { addEventListener: function(callback) {
onStart = callback || null; onStartListener = callback || null;
if ( onStart !== null ) { if ( typeof onStartListener === 'function' ) {
updateCycleTime = Date.now() + updateCycleFirstPeriod; updateCycleTime = Date.now() + updateCycleFirstPeriod;
} }
} }
@ -1209,9 +1256,17 @@ exports.onStart = {
/******************************************************************************/ /******************************************************************************/
exports.onAssetUpdated = {
addEventListener: function(callback) {
onAssetUpdatedListener = callback || null;
}
};
/******************************************************************************/
exports.onCompleted = { exports.onCompleted = {
addEventListener: function(callback) { addEventListener: function(callback) {
onCompleted = callback || null; onCompletedListener = callback || null;
} }
}; };

View File

@ -83,6 +83,12 @@ return {
allowedRequestCount: 0 allowedRequestCount: 0
}, },
// read-only
systemSettings: {
compiledMagic: 'dgycowxrdjuf',
selfieMagic: 'dmakcrbecglp'
},
// EasyList, EasyPrivacy and many others have an 4-day update period, // EasyList, EasyPrivacy and many others have an 4-day update period,
// as per list headers. // as per list headers.
updateAssetsEvery: 97 * oneHour, updateAssetsEvery: 97 * oneHour,
@ -111,7 +117,6 @@ return {
remoteBlacklists: { remoteBlacklists: {
}, },
selfieMagic: 'bizhviclttie',
selfieAfter: 23 * oneMinute, selfieAfter: 23 * oneMinute,
pageStores: {}, pageStores: {},

View File

@ -334,7 +334,6 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
hash = href.slice(pos + 3, pos + 11); hash = href.slice(pos + 3, pos + 11);
selectors = generics[hash]; selectors = generics[hash];
if ( selectors === undefined ) { continue; } if ( selectors === undefined ) { continue; }
selectors = selectors.split(',\n');
iSelector = selectors.length; iSelector = selectors.length;
while ( iSelector-- ) { while ( iSelector-- ) {
selector = selectors[iSelector]; selector = selectors[iSelector];

View File

@ -71,8 +71,6 @@ var cosmeticFilters = function(details) {
var hide = details.cosmeticHide; var hide = details.cosmeticHide;
var i; var i;
if ( donthide.length !== 0 ) { if ( donthide.length !== 0 ) {
donthide = donthide.length !== 1 ? donthide.join(',\n') : donthide[0];
donthide = donthide.split(',\n');
i = donthide.length; i = donthide.length;
while ( i-- ) { while ( i-- ) {
donthideCosmeticFilters[donthide[i]] = true; donthideCosmeticFilters[donthide[i]] = true;
@ -80,8 +78,6 @@ var cosmeticFilters = function(details) {
} }
// https://github.com/gorhill/uBlock/issues/143 // https://github.com/gorhill/uBlock/issues/143
if ( hide.length !== 0 ) { if ( hide.length !== 0 ) {
hide = hide.length !== 1 ? hide.join(',\n') : hide[0];
hide = hide.split(',\n');
i = hide.length; i = hide.length;
var selector; var selector;
while ( i-- ) { while ( i-- ) {

View File

@ -481,7 +481,7 @@ var makeHash = function(unhide, token, mask) {
if ( unhide !== 0 ) { if ( unhide !== 0 ) {
hval |= 0x20000; hval |= 0x20000;
} }
return String.fromCharCode(hval >>> 9, hval & 0x1FF); return hval.toString(36);
}; };
/******************************************************************************/ /******************************************************************************/
@ -503,8 +503,8 @@ var makeHash = function(unhide, token, mask) {
// Specific filers can be enforced before the main document is loaded. // Specific filers can be enforced before the main document is loaded.
var FilterContainer = function() { var FilterContainer = function() {
this.domainHashMask = (1 << 10) - 1; this.domainHashMask = (1 << 10) - 1; // 10 bits
this.genericHashMask = (1 << 15) - 1; this.genericHashMask = (1 << 15) - 1; // 15 bits
this.type0NoDomainHash = 'type0NoDomain'; this.type0NoDomainHash = 'type0NoDomain';
this.type1NoDomainHash = 'type1NoDomain'; this.type1NoDomainHash = 'type1NoDomain';
this.parser = new FilterParser(); this.parser = new FilterParser();
@ -521,23 +521,15 @@ var FilterContainer = function() {
FilterContainer.prototype.reset = function() { FilterContainer.prototype.reset = function() {
this.parser.reset(); this.parser.reset();
this.µburi = µb.URI;
this.frozen = false; this.frozen = false;
this.acceptedCount = 0; this.acceptedCount = 0;
this.duplicateCount = 0; this.duplicateCount = 0;
this.duplicateBuster = {};
this.selectorCache = {}; this.selectorCache = {};
this.selectorCacheCount = 0; this.selectorCacheCount = 0;
// temporary (at parse time)
this.lowGenericHide = {};
this.lowGenericDonthide = {};
this.highGenericHide = {};
this.highGenericDonthide = {};
this.hostnameHide = {};
this.hostnameDonthide = {};
this.entityHide = {};
this.entityDonthide = {};
// permanent // permanent
// [class], [id] // [class], [id]
this.lowGenericFilters = {}; this.lowGenericFilters = {};
@ -555,9 +547,11 @@ FilterContainer.prototype.reset = function() {
this.highMediumGenericDonthideCount = 0; this.highMediumGenericDonthideCount = 0;
// everything else // everything else
this.highHighGenericHideArray = [];
this.highHighGenericHide = ''; this.highHighGenericHide = '';
this.highHighGenericDonthide = '';
this.highHighGenericHideCount = 0; this.highHighGenericHideCount = 0;
this.highHighGenericDonthideArray = [];
this.highHighGenericDonthide = '';
this.highHighGenericDonthideCount = 0; this.highHighGenericDonthideCount = 0;
// hostname, entity-based filters // hostname, entity-based filters
@ -567,7 +561,7 @@ FilterContainer.prototype.reset = function() {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.add = function(s) { FilterContainer.prototype.compile = function(s, out) {
var parsed = this.parser.parse(s); var parsed = this.parser.parse(s);
if ( parsed.invalid ) { if ( parsed.invalid ) {
return false; return false;
@ -576,9 +570,8 @@ FilterContainer.prototype.add = function(s) {
var hostnames = parsed.hostnames; var hostnames = parsed.hostnames;
var i = hostnames.length; var i = hostnames.length;
if ( i === 0 ) { if ( i === 0 ) {
this.addGenericSelector(parsed); this.compileGenericSelector(parsed, out);
return true; } else {
}
// https://github.com/gorhill/uBlock/issues/151 // https://github.com/gorhill/uBlock/issues/151
// Negated hostname means the filter applies to all non-negated hostnames // Negated hostname means the filter applies to all non-negated hostnames
// of same filter OR globally if there is no non-negated hostnames. // of same filter OR globally if there is no non-negated hostnames.
@ -590,257 +583,271 @@ FilterContainer.prototype.add = function(s) {
applyGlobally = false; applyGlobally = false;
} }
if ( hostname.slice(-2) === '.*' ) { if ( hostname.slice(-2) === '.*' ) {
this.addEntitySelector(hostname, parsed); this.compileEntitySelector(hostname, parsed, out);
} else { } else {
this.addHostnameSelector(hostname, parsed); this.compileHostnameSelector(hostname, parsed, out);
} }
} }
if ( applyGlobally ) { if ( applyGlobally ) {
this.addGenericSelector(parsed); this.compileGenericSelector(parsed, out);
} }
}
return true; return true;
}; };
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.addGenericSelector = function(parsed) { FilterContainer.prototype.compileGenericSelector = function(parsed, out) {
var entries; var selector = parsed.suffix;
var selectorType = parsed.suffix.charAt(0); var type = selector.charAt(0);
if ( selectorType === '#' || selectorType === '.' ) { var matches;
entries = parsed.unhide === 0 ?
this.lowGenericHide : if ( type === '#' || type === '.' ) {
this.lowGenericDonthide; matches = this.rePlainSelector.exec(selector);
} else { if ( matches === null ) {
entries = parsed.unhide === 0 ? return;
this.highGenericHide :
this.highGenericDonthide;
} }
if ( entries[parsed.suffix] === undefined ) { out.push(
entries[parsed.suffix] = true; 'c\v' +
} else { (matches[1] === selector ? 'lg\v' : 'lg+\v') +
//console.log('cosmetic-filtering.js > FilterContainer.addGenericSelector(): duplicate filter "%s"', parsed.suffix); makeHash(parsed.unhide, matches[1], this.genericHashMask) + '\v' +
this.duplicateCount += 1; selector
);
return;
} }
this.acceptedCount += 1;
// ["title"] and ["alt"] will go in high-low generic bin.
if ( this.reHighLow.test(selector) ) {
out.push(
'c\v' +
(parsed.unhide === 0 ? 'hlg0\v' : 'hlg1\v') +
selector
);
return;
}
// [href^="..."] will go in high-medium generic bin.
matches = this.reHighMedium.exec(selector);
if ( matches && matches.length === 2 ) {
out.push(
'c\v' +
(parsed.unhide === 0 ? 'hmg0\v' : 'hmg1\v') +
matches[1] + '\v' +
selector
);
return;
}
// All else
out.push(
'c\v' +
(parsed.unhide === 0 ? 'hhg0\v' : 'hhg1\v') +
selector
);
}; };
FilterContainer.prototype.rePlainSelector = /^([#.][\w-]+)/;
FilterContainer.prototype.reHighLow = /^[a-z]*\[(?:alt|title)="[^"]+"\]$/;
FilterContainer.prototype.reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/;
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.addHostnameSelector = function(hostname, parsed) { FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, out) {
// https://github.com/gorhill/uBlock/issues/145 // https://github.com/gorhill/uBlock/issues/145
var unhide = parsed.unhide; var unhide = parsed.unhide;
if ( hostname.charAt(0) === '~' ) { if ( hostname.charAt(0) === '~' ) {
hostname = hostname.slice(1); hostname = hostname.slice(1);
unhide ^= 1; unhide ^= 1;
} }
var entries = unhide === 0 ?
this.hostnameHide :
this.hostnameDonthide;
var entry = entries[hostname];
if ( entry === undefined ) {
entry = entries[hostname] = {};
entry[parsed.suffix] = true;
} else if ( entry[parsed.suffix] === undefined ) {
entry[parsed.suffix] = true;
} else {
//console.log('cosmetic-filtering.js > FilterContainer.addHostnameSelector(): duplicate filter "%s"', parsed.suffix);
this.duplicateCount += 1;
}
this.acceptedCount += 1;
};
/******************************************************************************/
FilterContainer.prototype.addEntitySelector = function(hostname, parsed) {
var entries = parsed.unhide === 0 ?
this.entityHide :
this.entityDonthide;
var entity = hostname.slice(0, -2);
var entry = entries[entity];
if ( entry === undefined ) {
entry = entries[entity] = {};
entry[parsed.suffix] = true;
} else if ( entry[parsed.suffix] === undefined ) {
entry[parsed.suffix] = true;
} else {
//console.log('cosmetic-filtering.js > FilterContainer.addEntitySelector(): duplicate filter "%s"', parsed.suffix);
this.duplicateCount += 1;
}
this.acceptedCount += 1;
};
/******************************************************************************/
FilterContainer.prototype.freezeLowGenerics = function(what, type) {
var selectors = this[what];
var matches, selectorPrefix, f, hash, bucket;
for ( var selector in selectors ) {
if ( selectors.hasOwnProperty(selector) === false ) {
continue;
}
matches = this.rePlainSelector.exec(selector);
if ( !matches ) {
continue;
}
selectorPrefix = matches[1];
f = selectorPrefix === selector ?
new FilterPlain(selector) :
new FilterPlainMore(selector);
hash = makeHash(type, selectorPrefix, this.genericHashMask);
bucket = this.lowGenericFilters[hash];
if ( bucket === undefined ) {
this.lowGenericFilters[hash] = f;
} else if ( bucket instanceof FilterBucket ) {
bucket.add(f);
} else {
this.lowGenericFilters[hash] = new FilterBucket(bucket, f);
}
}
this[what] = {};
};
FilterContainer.prototype.rePlainSelector = /^([#.][\w-]+)/;
/******************************************************************************/
FilterContainer.prototype.freezeHostnameSpecifics = function(what, type) {
var µburi = µb.URI;
var entries = this[what];
var filters = this.hostnameFilters;
var f, domain, hash, bucket;
for ( var hostname in entries ) {
if ( entries.hasOwnProperty(hostname) === false ) {
continue;
}
f = new FilterHostname(Object.keys(entries[hostname]).join(',\n'), hostname);
// https://github.com/gorhill/uBlock/issues/188 // https://github.com/gorhill/uBlock/issues/188
// If not a real domain as per PSL, assign a synthetic one // If not a real domain as per PSL, assign a synthetic one
domain = µburi.domainFromHostname(hostname); var hash;
var domain = this.µburi.domainFromHostname(hostname);
if ( domain === '' ) { if ( domain === '' ) {
hash = type === 0 ? this.type0NoDomainHash : this.type1NoDomainHash; hash = unhide === 0 ? this.type0NoDomainHash : this.type1NoDomainHash;
} else { } else {
hash = makeHash(type, domain, this.domainHashMask); hash = makeHash(unhide, domain, this.domainHashMask);
} }
bucket = filters[hash]; out.push(
if ( bucket === undefined ) { 'c\v' +
filters[hash] = f; 'h\v' +
} else if ( bucket instanceof FilterBucket ) { hash + '\v' +
bucket.add(f); hostname + '\v' +
} else { parsed.suffix
filters[hash] = new FilterBucket(bucket, f); );
}
}
this[what] = {};
}; };
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.freezeEntitySpecifics = function(what, type) { FilterContainer.prototype.compileEntitySelector = function(hostname, parsed, out) {
var entries = this[what]; var entity = hostname.slice(0, -2);
var filters = this.entityFilters; out.push(
var f, hash, bucket; 'c\v' +
for ( var entity in entries ) { 'e\v' +
if ( entries.hasOwnProperty(entity) === false ) { entity + '\v' +
continue; parsed.suffix
} );
f = new FilterEntity(Object.keys(entries[entity]).join(',\n'), entity);
hash = makeHash(type, entity, this.domainHashMask);
bucket = filters[hash];
if ( bucket === undefined ) {
filters[hash] = f;
} else if ( bucket instanceof FilterBucket ) {
bucket.add(f);
} else {
filters[hash] = new FilterBucket(bucket, f);
}
}
this[what] = {};
}; };
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.freezeHighGenerics = function(what) { FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) {
var selectors = this['highGeneric' + what]; if ( skip ) {
return this.skipCompiledContent(text, lineBeg);
// ["title"] and ["alt"] will go in high-low generic bin.
var highLowGenericProp = 'highLowGeneric' + what;
var highLowGeneric = this[highLowGenericProp];
var highLowGenericCount = 0;
// [href^="..."] will go in high-medium generic bin.
var highMediumGenericProp = 'highMediumGeneric' + what;
var highMediumGeneric = this[highMediumGenericProp];
var highMediumGenericCount = 0;
// The rest will be put in the high-high generic bin.
// https://github.com/gorhill/uBlock/issues/236
// Insert whatever we already have
var highHighGeneric = [];
var highHighGenericProp = 'highHighGeneric' + what;
if ( this[highHighGenericProp] !== '' ) {
highHighGeneric.push(this[highHighGenericProp]);
} }
var highHighGenericCount = 0;
// https://github.com/gorhill/uBlock/issues/456 var lineEnd;
// Include tag name, it's part of the filter var textEnd = text.length;
var reHighLow = /^[a-z]*\[(?:alt|title)="[^"]+"\]$/; var line, fields, filter, bucket;
var reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/;
var matches, hash;
for ( var selector in selectors ) { while ( lineBeg < textEnd ) {
if ( selectors.hasOwnProperty(selector) === false ) { if ( text.charAt(lineBeg) !== 'c' ) {
return lineBeg;
}
lineEnd = text.indexOf('\n', lineBeg);
if ( lineEnd === -1 ) {
lineEnd = textEnd;
}
line = text.slice(lineBeg + 2, lineEnd);
lineBeg = lineEnd + 1;
this.acceptedCount += 1;
if ( this.duplicateBuster.hasOwnProperty(line) ) {
this.duplicateCount += 1;
continue; continue;
} }
// ["title"] and ["alt"] will go in high-low generic bin. this.duplicateBuster[line] = true;
matches = reHighLow.exec(selector);
if ( matches && matches.length === 1 ) { fields = line.split('\v');
highLowGeneric[matches[0]] = true;
highLowGenericCount += 1; // h ir twitter.com .promoted-tweet
continue; if ( fields[0] === 'h' ) {
} filter = new FilterHostname(fields[3], fields[2]);
// [href^="..."] will go in high-medium generic bin. bucket = this.hostnameFilters[fields[1]];
matches = reHighMedium.exec(selector); if ( bucket === undefined ) {
if ( matches && matches.length === 2 ) { this.hostnameFilters[fields[1]] = filter;
hash = matches[1]; } else if ( bucket instanceof FilterBucket ) {
if ( highMediumGeneric[hash] === undefined ) { bucket.add(filter);
highMediumGeneric[hash] = matches[0];
} else { } else {
highMediumGeneric[hash] += ',\n' + matches[0]; this.hostnameFilters[fields[1]] = new FilterBucket(bucket, filter);
} }
highMediumGenericCount += 1;
continue; continue;
} }
// All else
highHighGeneric.push(selector); // lg 105 .largeAd
highHighGenericCount += 1; // lg+ 2jx .Mpopup + #Mad > #MadZone
if ( fields[0] === 'lg' || fields[0] === 'lg+' ) {
filter = fields[0] === 'lg' ?
new FilterPlain(fields[2]) :
new FilterPlainMore(fields[2]);
bucket = this.lowGenericFilters[fields[1]];
if ( bucket === undefined ) {
this.lowGenericFilters[fields[1]] = filter;
} else if ( bucket instanceof FilterBucket ) {
bucket.add(filter);
} else {
this.lowGenericFilters[fields[1]] = new FilterBucket(bucket, filter);
}
continue;
} }
this[highLowGenericProp + 'Count'] += highLowGenericCount; // entity selector
this[highMediumGenericProp + 'Count'] += highMediumGenericCount; if ( fields[0] === 'e' ) {
this[highHighGenericProp] = highHighGeneric.join(',\n'); bucket = this.entityFilters[fields[1]];
this[highHighGenericProp + 'Count'] += highHighGenericCount; if ( bucket === undefined ) {
this.entityFilters[fields[1]] = [fields[2]];
} else {
bucket.push(fields[2]);
}
continue;
}
// Empty cumulated selectors if ( fields[0] === 'hlg0' ) {
this['highGeneric' + what] = {}; this.highLowGenericHide[fields[1]] = true;
this.highLowGenericHideCount += 1;
continue;
}
if ( fields[0] === 'hlg1' ) {
this.highLowGenericDonthide[fields[1]] = true;
this.highLowGenericDonthideCount += 1;
continue;
}
if ( fields[0] === 'hmg0' ) {
if ( Array.isArray(this.highMediumGenericHide[fields[1]]) ) {
this.highMediumGenericHide[fields[1]].push(fields[2]);
} else {
this.highMediumGenericHide[fields[1]] = [fields[2]];
}
this.highMediumGenericHideCount += 1;
continue;
}
if ( fields[0] === 'hmg1' ) {
if ( Array.isArray(this.highMediumGenericDonthide[fields[1]]) ) {
this.highMediumGenericDonthide[fields[1]].push(fields[2]);
} else {
this.highMediumGenericDonthide[fields[1]] = [fields[2]];
}
this.highMediumGenericDonthideCount += 1;
continue;
}
if ( fields[0] === 'hhg0' ) {
this.highHighGenericHideArray.push(fields[1]);
this.highHighGenericHideCount += 1;
continue;
}
if ( fields[0] === 'hhg1' ) {
this.highHighGenericDonthideArray.push(fields[1]);
this.highHighGenericDonthideCount += 1;
continue;
}
}
return textEnd;
};
/******************************************************************************/
FilterContainer.prototype.skipCompiledContent = function(text, lineBeg) {
var lineEnd;
var textEnd = text.length;
while ( lineBeg < textEnd ) {
if ( text.charAt(lineBeg) !== 'c' ) {
return lineBeg;
}
lineEnd = text.indexOf('\n', lineBeg);
if ( lineEnd === -1 ) {
lineEnd = textEnd;
}
lineBeg = lineEnd + 1;
}
return textEnd;
}; };
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.freeze = function() { FilterContainer.prototype.freeze = function() {
this.freezeLowGenerics('lowGenericHide', 0); this.duplicateBuster = {};
this.freezeLowGenerics('lowGenericDonthide', 1);
this.freezeHighGenerics('Hide'); if ( this.highHighGenericHide !== '' ) {
this.freezeHighGenerics('Donthide'); this.highHighGenericHideArray.unshift(this.highHighGenericHide);
this.freezeHostnameSpecifics('hostnameHide', 0); }
this.freezeHostnameSpecifics('hostnameDonthide', 1); this.highHighGenericHide = this.highHighGenericHideArray.join(',\n');
this.freezeEntitySpecifics('entityHide', 0); this.highHighGenericHideArray = [];
this.freezeEntitySpecifics('entityDonthide', 1); if ( this.highHighGenericDonthide !== '' ) {
this.highHighGenericDonthideArray.unshift(this.highHighGenericDonthide);
}
this.highHighGenericDonthide = this.highHighGenericDonthideArray.join(',\n');
this.highHighGenericDonthideArray = [];
this.parser.reset(); this.parser.reset();
this.frozen = true; this.frozen = true;
//histogram('lowGenericFilters', this.lowGenericFilters);
//histogram('hostnameFilters', this.hostnameFilters);
}; };
/******************************************************************************/ /******************************************************************************/
@ -875,7 +882,7 @@ FilterContainer.prototype.toSelfie = function() {
acceptedCount: this.acceptedCount, acceptedCount: this.acceptedCount,
duplicateCount: this.duplicateCount, duplicateCount: this.duplicateCount,
hostnameSpecificFilters: selfieFromDict(this.hostnameFilters), hostnameSpecificFilters: selfieFromDict(this.hostnameFilters),
entitySpecificFilters: selfieFromDict(this.entityFilters), entitySpecificFilters: this.entityFilters,
lowGenericFilters: selfieFromDict(this.lowGenericFilters), lowGenericFilters: selfieFromDict(this.lowGenericFilters),
highLowGenericHide: this.highLowGenericHide, highLowGenericHide: this.highLowGenericHide,
highLowGenericDonthide: this.highLowGenericDonthide, highLowGenericDonthide: this.highLowGenericDonthide,
@ -940,7 +947,7 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
this.acceptedCount = selfie.acceptedCount; this.acceptedCount = selfie.acceptedCount;
this.duplicateCount = selfie.duplicateCount; this.duplicateCount = selfie.duplicateCount;
this.hostnameFilters = dictFromSelfie(selfie.hostnameSpecificFilters); this.hostnameFilters = dictFromSelfie(selfie.hostnameSpecificFilters);
this.entityFilters = dictFromSelfie(selfie.entitySpecificFilters); this.entityFilters = selfie.entitySpecificFilters;
this.lowGenericFilters = dictFromSelfie(selfie.lowGenericFilters); this.lowGenericFilters = dictFromSelfie(selfie.lowGenericFilters);
this.highLowGenericHide = selfie.highLowGenericHide; this.highLowGenericHide = selfie.highLowGenericHide;
this.highLowGenericDonthide = selfie.highLowGenericDonthide; this.highLowGenericDonthide = selfie.highLowGenericDonthide;
@ -1156,14 +1163,18 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request) {
if ( bucket = this.hostnameFilters[this.type0NoDomainHash] ) { if ( bucket = this.hostnameFilters[this.type0NoDomainHash] ) {
bucket.retrieve(hostname, r.cosmeticHide); bucket.retrieve(hostname, r.cosmeticHide);
} }
hash = makeHash(0, r.entity, this.domainHashMask);
if ( bucket = this.entityFilters[hash] ) { // entity filter buckets are always plain js array
bucket.retrieve(pos === -1 ? domain : hostname.slice(0, pos - domain.length), r.cosmeticHide); if ( bucket = this.entityFilters[r.entity] ) {
r.cosmeticHide = r.cosmeticHide.concat(bucket);
} }
// No entity exceptions as of now
hash = makeHash(1, domain, this.domainHashMask); hash = makeHash(1, domain, this.domainHashMask);
if ( bucket = this.hostnameFilters[hash] ) { if ( bucket = this.hostnameFilters[hash] ) {
bucket.retrieve(hostname, r.cosmeticDonthide); bucket.retrieve(hostname, r.cosmeticDonthide);
} }
// https://github.com/gorhill/uBlock/issues/188 // https://github.com/gorhill/uBlock/issues/188
// Special bucket for those filters without a valid domain name as per PSL // Special bucket for those filters without a valid domain name as per PSL
if ( bucket = this.hostnameFilters[this.type1NoDomainHash] ) { if ( bucket = this.hostnameFilters[this.type1NoDomainHash] ) {

View File

@ -68,7 +68,7 @@ var onMessage = function(request, sender, callback) {
break; break;
case 'reloadAllFilters': case 'reloadAllFilters':
µb.reloadPresetBlacklists(request.switches, request.update); µb.reloadFilterLists(request.switches, request.update);
break; break;
case 'reloadTab': case 'reloadTab':

View File

@ -20,11 +20,12 @@
*/ */
/* exported quickProfiler */ /* exported quickProfiler */
'use strict';
/******************************************************************************/ /******************************************************************************/
var quickProfiler = (function() { var quickProfiler = (function() {
'use strict';
var timer = window.performance || Date; var timer = window.performance || Date;
var time = 0; var time = 0;
var count = 0; var count = 0;

View File

@ -27,6 +27,8 @@
(function() { (function() {
quickProfiler.start('start.js');
/******************************************************************************/ /******************************************************************************/
// Final initialization steps after all needed assets are in memory. // Final initialization steps after all needed assets are in memory.
@ -40,11 +42,15 @@ var onAllReady = function() {
// Check for updates not too far in the future. // Check for updates not too far in the future.
µb.assetUpdater.onStart.addEventListener(µb.updateStartHandler.bind(µb)); µb.assetUpdater.onStart.addEventListener(µb.updateStartHandler.bind(µb));
µb.assetUpdater.onCompleted.addEventListener(µb.updateCompleteHandler.bind(µb)); µb.assetUpdater.onCompleted.addEventListener(µb.updateCompleteHandler.bind(µb));
µb.assetUpdater.onAssetUpdated.addEventListener(µb.assetUpdatedHandler.bind(µb));
µb.assets.onAssetCacheRemoved.addEventListener(µb.assetCacheRemovedHandler.bind(µb));
// Important: remove barrier to remote fetching, this was useful only // Important: remove barrier to remote fetching, this was useful only
// for launch time. // for launch time.
µb.assets.allowRemoteFetch = true; µb.assets.allowRemoteFetch = true;
quickProfiler.stop(0);
vAPI.onLoadAllCompleted(); vAPI.onLoadAllCompleted();
}; };
@ -155,9 +161,34 @@ var onUserSettingsReady = function(userSettings) {
µb.XAL.keyvalRemoveOne('logRequests'); µb.XAL.keyvalRemoveOne('logRequests');
}; };
µBlock.loadUserSettings(onUserSettingsReady); /******************************************************************************/
µBlock.loadWhitelist(onWhitelistReady);
µBlock.loadLocalSettings(); // Housekeeping, as per system setting changes
var onSystemSettingsReady = function(system) {
var µb = µBlock;
var mustSaveSystemSettings = false;
if ( system.compiledMagic !== µb.systemSettings.compiledMagic ) {
µb.assets.purge(/^cache:\/\/compiled-/);
mustSaveSystemSettings = true;
}
if ( system.selfieMagic !== µb.systemSettings.selfieMagic ) {
µb.destroySelfie();
mustSaveSystemSettings = true;
}
if ( mustSaveSystemSettings ) {
µb.saveSystemSettings();
}
µb.loadUserSettings(onUserSettingsReady);
µb.loadWhitelist(onWhitelistReady);
µb.loadLocalSettings();
};
/******************************************************************************/
µBlock.loadSystemSettings(onSystemSettingsReady);
/******************************************************************************/ /******************************************************************************/

View File

@ -227,7 +227,7 @@ FilterPlain.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s; return url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s;
}; };
FilterPlain.prototype.fid = 'a'; FilterPlain.fid = FilterPlain.prototype.fid = 'a';
FilterPlain.prototype.toString = function() { FilterPlain.prototype.toString = function() {
return this.s; return this.s;
@ -238,6 +238,10 @@ FilterPlain.prototype.toSelfie = function() {
this.tokenBeg; this.tokenBeg;
}; };
FilterPlain.compile = function(details) {
return details.f + '\t' + details.tokenBeg;
};
FilterPlain.fromSelfie = function(s) { FilterPlain.fromSelfie = function(s) {
var pos = s.indexOf('\t'); var pos = s.indexOf('\t');
return new FilterPlain(s.slice(0, pos), atoi(s.slice(pos + 1))); return new FilterPlain(s.slice(0, pos), atoi(s.slice(pos + 1)));
@ -256,7 +260,7 @@ FilterPlainHostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s; url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s;
}; };
FilterPlainHostname.prototype.fid = 'ah'; FilterPlainHostname.fid = FilterPlainHostname.prototype.fid = 'ah';
FilterPlainHostname.prototype.toString = function() { FilterPlainHostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname; return this.s + '$domain=' + this.hostname;
@ -268,6 +272,12 @@ FilterPlainHostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterPlainHostname.compile = function(details, hostname) {
return details.f + '\t' +
details.tokenBeg + '\t' +
hostname;
};
FilterPlainHostname.fromSelfie = function(s) { FilterPlainHostname.fromSelfie = function(s) {
var args = s.split('\t'); var args = s.split('\t');
return new FilterPlainHostname(args[0], atoi(args[1]), args[2]); return new FilterPlainHostname(args[0], atoi(args[1]), args[2]);
@ -283,7 +293,7 @@ FilterPlainPrefix0.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg, this.s.length) === this.s; return url.substr(tokenBeg, this.s.length) === this.s;
}; };
FilterPlainPrefix0.prototype.fid = '0a'; FilterPlainPrefix0.fid = FilterPlainPrefix0.prototype.fid = '0a';
FilterPlainPrefix0.prototype.toString = function() { FilterPlainPrefix0.prototype.toString = function() {
return this.s; return this.s;
@ -293,6 +303,10 @@ FilterPlainPrefix0.prototype.toSelfie = function() {
return this.s; return this.s;
}; };
FilterPlainPrefix0.compile = function(details) {
return details.f;
};
FilterPlainPrefix0.fromSelfie = function(s) { FilterPlainPrefix0.fromSelfie = function(s) {
return new FilterPlainPrefix0(s); return new FilterPlainPrefix0(s);
}; };
@ -309,7 +323,7 @@ FilterPlainPrefix0Hostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg, this.s.length) === this.s; url.substr(tokenBeg, this.s.length) === this.s;
}; };
FilterPlainPrefix0Hostname.prototype.fid = '0ah'; FilterPlainPrefix0Hostname.fid = FilterPlainPrefix0Hostname.prototype.fid = '0ah';
FilterPlainPrefix0Hostname.prototype.toString = function() { FilterPlainPrefix0Hostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname; return this.s + '$domain=' + this.hostname;
@ -320,6 +334,10 @@ FilterPlainPrefix0Hostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterPlainPrefix0Hostname.compile = function(details, hostname) {
return details.f + '\t' + hostname;
};
FilterPlainPrefix0Hostname.fromSelfie = function(s) { FilterPlainPrefix0Hostname.fromSelfie = function(s) {
var pos = s.indexOf('\t'); var pos = s.indexOf('\t');
return new FilterPlainPrefix0Hostname(s.slice(0, pos), s.slice(pos + 1)); return new FilterPlainPrefix0Hostname(s.slice(0, pos), s.slice(pos + 1));
@ -335,7 +353,7 @@ FilterPlainPrefix1.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg - 1, this.s.length) === this.s; return url.substr(tokenBeg - 1, this.s.length) === this.s;
}; };
FilterPlainPrefix1.prototype.fid = '1a'; FilterPlainPrefix1.fid = FilterPlainPrefix1.prototype.fid = '1a';
FilterPlainPrefix1.prototype.toString = function() { FilterPlainPrefix1.prototype.toString = function() {
return this.s; return this.s;
@ -345,6 +363,10 @@ FilterPlainPrefix1.prototype.toSelfie = function() {
return this.s; return this.s;
}; };
FilterPlainPrefix1.compile = function(details) {
return details.f;
};
FilterPlainPrefix1.fromSelfie = function(s) { FilterPlainPrefix1.fromSelfie = function(s) {
return new FilterPlainPrefix1(s); return new FilterPlainPrefix1(s);
}; };
@ -361,7 +383,7 @@ FilterPlainPrefix1Hostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg - 1, this.s.length) === this.s; url.substr(tokenBeg - 1, this.s.length) === this.s;
}; };
FilterPlainPrefix1Hostname.prototype.fid = '1ah'; FilterPlainPrefix1Hostname.fid = FilterPlainPrefix1Hostname.prototype.fid = '1ah';
FilterPlainPrefix1Hostname.prototype.toString = function() { FilterPlainPrefix1Hostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname; return this.s + '$domain=' + this.hostname;
@ -372,6 +394,10 @@ FilterPlainPrefix1Hostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterPlainPrefix1Hostname.compile = function(details, hostname) {
return details.f + '\t' + hostname;
};
FilterPlainPrefix1Hostname.fromSelfie = function(s) { FilterPlainPrefix1Hostname.fromSelfie = function(s) {
var pos = s.indexOf('\t'); var pos = s.indexOf('\t');
return new FilterPlainPrefix1Hostname(s.slice(0, pos), s.slice(pos + 1)); return new FilterPlainPrefix1Hostname(s.slice(0, pos), s.slice(pos + 1));
@ -387,7 +413,7 @@ FilterPlainLeftAnchored.prototype.match = function(url) {
return url.slice(0, this.s.length) === this.s; return url.slice(0, this.s.length) === this.s;
}; };
FilterPlainLeftAnchored.prototype.fid = '|a'; FilterPlainLeftAnchored.fid = FilterPlainLeftAnchored.prototype.fid = '|a';
FilterPlainLeftAnchored.prototype.toString = function() { FilterPlainLeftAnchored.prototype.toString = function() {
return '|' + this.s; return '|' + this.s;
@ -397,6 +423,10 @@ FilterPlainLeftAnchored.prototype.toSelfie = function() {
return this.s; return this.s;
}; };
FilterPlainLeftAnchored.compile = function(details) {
return details.f;
};
FilterPlainLeftAnchored.fromSelfie = function(s) { FilterPlainLeftAnchored.fromSelfie = function(s) {
return new FilterPlainLeftAnchored(s); return new FilterPlainLeftAnchored(s);
}; };
@ -413,7 +443,7 @@ FilterPlainLeftAnchoredHostname.prototype.match = function(url) {
url.slice(0, this.s.length) === this.s; url.slice(0, this.s.length) === this.s;
}; };
FilterPlainLeftAnchoredHostname.prototype.fid = '|ah'; FilterPlainLeftAnchoredHostname.fid = FilterPlainLeftAnchoredHostname.prototype.fid = '|ah';
FilterPlainLeftAnchoredHostname.prototype.toString = function() { FilterPlainLeftAnchoredHostname.prototype.toString = function() {
return '|' + this.s + '$domain=' + this.hostname; return '|' + this.s + '$domain=' + this.hostname;
@ -424,6 +454,10 @@ FilterPlainLeftAnchoredHostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterPlainLeftAnchoredHostname.compile = function(details, hostname) {
return details.f + '\t' + hostname;
};
FilterPlainLeftAnchoredHostname.fromSelfie = function(s) { FilterPlainLeftAnchoredHostname.fromSelfie = function(s) {
var pos = s.indexOf('\t'); var pos = s.indexOf('\t');
return new FilterPlainLeftAnchoredHostname(s.slice(0, pos), s.slice(pos + 1)); return new FilterPlainLeftAnchoredHostname(s.slice(0, pos), s.slice(pos + 1));
@ -439,7 +473,7 @@ FilterPlainRightAnchored.prototype.match = function(url) {
return url.slice(-this.s.length) === this.s; return url.slice(-this.s.length) === this.s;
}; };
FilterPlainRightAnchored.prototype.fid = 'a|'; FilterPlainRightAnchored.fid = FilterPlainRightAnchored.prototype.fid = 'a|';
FilterPlainRightAnchored.prototype.toString = function() { FilterPlainRightAnchored.prototype.toString = function() {
return this.s + '|'; return this.s + '|';
@ -449,6 +483,10 @@ FilterPlainRightAnchored.prototype.toSelfie = function() {
return this.s; return this.s;
}; };
FilterPlainRightAnchored.compile = function(details) {
return details.f;
};
FilterPlainRightAnchored.fromSelfie = function(s) { FilterPlainRightAnchored.fromSelfie = function(s) {
return new FilterPlainRightAnchored(s); return new FilterPlainRightAnchored(s);
}; };
@ -465,7 +503,7 @@ FilterPlainRightAnchoredHostname.prototype.match = function(url) {
url.slice(-this.s.length) === this.s; url.slice(-this.s.length) === this.s;
}; };
FilterPlainRightAnchoredHostname.prototype.fid = 'a|h'; FilterPlainRightAnchoredHostname.fid = FilterPlainRightAnchoredHostname.prototype.fid = 'a|h';
FilterPlainRightAnchoredHostname.prototype.toString = function() { FilterPlainRightAnchoredHostname.prototype.toString = function() {
return this.s + '|$domain=' + this.hostname; return this.s + '|$domain=' + this.hostname;
@ -476,6 +514,10 @@ FilterPlainRightAnchoredHostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterPlainRightAnchoredHostname.compile = function(details, hostname) {
return details.f + '\t' + hostname;
};
FilterPlainRightAnchoredHostname.fromSelfie = function(s) { FilterPlainRightAnchoredHostname.fromSelfie = function(s) {
var pos = s.indexOf('\t'); var pos = s.indexOf('\t');
return new FilterPlainRightAnchoredHostname(s.slice(0, pos), s.slice(pos + 1)); return new FilterPlainRightAnchoredHostname(s.slice(0, pos), s.slice(pos + 1));
@ -500,7 +542,7 @@ FilterPlainHnAnchored.prototype.match = function(url, tokenBeg) {
reURLPostHostnameAnchors.test(url.slice(pos + 3, tokenBeg)) === false; reURLPostHostnameAnchors.test(url.slice(pos + 3, tokenBeg)) === false;
}; };
FilterPlainHnAnchored.prototype.fid = 'h|a'; FilterPlainHnAnchored.fid = FilterPlainHnAnchored.prototype.fid = 'h|a';
FilterPlainHnAnchored.prototype.toString = function() { FilterPlainHnAnchored.prototype.toString = function() {
return '||' + this.s; return '||' + this.s;
@ -510,6 +552,10 @@ FilterPlainHnAnchored.prototype.toSelfie = function() {
return this.s; return this.s;
}; };
FilterPlainHnAnchored.compile = function(details) {
return details.f;
};
FilterPlainHnAnchored.fromSelfie = function(s) { FilterPlainHnAnchored.fromSelfie = function(s) {
return new FilterPlainHnAnchored(s); return new FilterPlainHnAnchored(s);
}; };
@ -535,7 +581,7 @@ FilterSingleWildcard.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0; url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
}; };
FilterSingleWildcard.prototype.fid = '*'; FilterSingleWildcard.fid = FilterSingleWildcard.prototype.fid = '*';
FilterSingleWildcard.prototype.toString = function() { FilterSingleWildcard.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment; return this.lSegment + '*' + this.rSegment;
@ -547,6 +593,14 @@ FilterSingleWildcard.prototype.toSelfie = function() {
this.tokenBeg; this.tokenBeg;
}; };
FilterSingleWildcard.compile = function(details) {
var s = details.f;
var pos = s.indexOf('*');
return s.slice(0, pos) + '\t' +
s.slice(pos + 1) + '\t' +
details.tokenBeg;
};
FilterSingleWildcard.fromSelfie = function(s) { FilterSingleWildcard.fromSelfie = function(s) {
var args = s.split('\t'); var args = s.split('\t');
return new FilterSingleWildcard(args[0], args[1], atoi(args[2])); return new FilterSingleWildcard(args[0], args[1], atoi(args[2]));
@ -568,7 +622,7 @@ FilterSingleWildcardHostname.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0; url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
}; };
FilterSingleWildcardHostname.prototype.fid = '*h'; FilterSingleWildcardHostname.fid = FilterSingleWildcardHostname.prototype.fid = '*h';
FilterSingleWildcardHostname.prototype.toString = function() { FilterSingleWildcardHostname.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname; return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
@ -581,6 +635,15 @@ FilterSingleWildcardHostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterSingleWildcardHostname.compile = function(details, hostname) {
var s = details.f;
var pos = s.indexOf('*');
return s.slice(0, pos) + '\t' +
s.slice(pos + 1) + '\t' +
details.tokenBeg + '\t' +
hostname;
};
FilterSingleWildcardHostname.fromSelfie = function(s) { FilterSingleWildcardHostname.fromSelfie = function(s) {
var args = s.split('\t'); var args = s.split('\t');
return new FilterSingleWildcardHostname(args[0], args[1], atoi(args[2]), args[3]); return new FilterSingleWildcardHostname(args[0], args[1], atoi(args[2]), args[3]);
@ -598,7 +661,7 @@ FilterSingleWildcardPrefix0.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0; url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
}; };
FilterSingleWildcardPrefix0.prototype.fid = '0*'; FilterSingleWildcardPrefix0.fid = FilterSingleWildcardPrefix0.prototype.fid = '0*';
FilterSingleWildcardPrefix0.prototype.toString = function() { FilterSingleWildcardPrefix0.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment; return this.lSegment + '*' + this.rSegment;
@ -609,6 +672,12 @@ FilterSingleWildcardPrefix0.prototype.toSelfie = function() {
this.rSegment; this.rSegment;
}; };
FilterSingleWildcardPrefix0.compile = function(details) {
var s = details.f;
var pos = s.indexOf('*');
return s.slice(0, pos) + '\t' + s.slice(pos + 1);
};
FilterSingleWildcardPrefix0.fromSelfie = function(s) { FilterSingleWildcardPrefix0.fromSelfie = function(s) {
var pos = s.indexOf('\t'); var pos = s.indexOf('\t');
return new FilterSingleWildcardPrefix0(s.slice(0, pos), s.slice(pos + 1)); return new FilterSingleWildcardPrefix0(s.slice(0, pos), s.slice(pos + 1));
@ -628,7 +697,7 @@ FilterSingleWildcardPrefix0Hostname.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0; url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
}; };
FilterSingleWildcardPrefix0Hostname.prototype.fid = '0*h'; FilterSingleWildcardPrefix0Hostname.fid = FilterSingleWildcardPrefix0Hostname.prototype.fid = '0*h';
FilterSingleWildcardPrefix0Hostname.prototype.toString = function() { FilterSingleWildcardPrefix0Hostname.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname; return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
@ -640,6 +709,14 @@ FilterSingleWildcardPrefix0Hostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterSingleWildcardPrefix0Hostname.compile = function(details, hostname) {
var s = details.f;
var pos = s.indexOf('*');
return s.slice(0, pos) + '\t' +
s.slice(pos + 1) + '\t' +
hostname;
};
FilterSingleWildcardPrefix0Hostname.fromSelfie = function(s) { FilterSingleWildcardPrefix0Hostname.fromSelfie = function(s) {
var args = s.split('\t'); var args = s.split('\t');
return new FilterSingleWildcardPrefix0Hostname(args[0], args[1], args[2]); return new FilterSingleWildcardPrefix0Hostname(args[0], args[1], args[2]);
@ -657,7 +734,7 @@ FilterSingleWildcardLeftAnchored.prototype.match = function(url) {
url.indexOf(this.rSegment, this.lSegment.length) > 0; url.indexOf(this.rSegment, this.lSegment.length) > 0;
}; };
FilterSingleWildcardLeftAnchored.prototype.fid = '|*'; FilterSingleWildcardLeftAnchored.fid = FilterSingleWildcardLeftAnchored.prototype.fid = '|*';
FilterSingleWildcardLeftAnchored.prototype.toString = function() { FilterSingleWildcardLeftAnchored.prototype.toString = function() {
return '|' + this.lSegment + '*' + this.rSegment; return '|' + this.lSegment + '*' + this.rSegment;
@ -668,6 +745,13 @@ FilterSingleWildcardLeftAnchored.prototype.toSelfie = function() {
this.rSegment; this.rSegment;
}; };
FilterSingleWildcardLeftAnchored.compile = function(details) {
var s = details.f;
var pos = s.indexOf('*');
return s.slice(0, pos) + '\t' +
s.slice(pos + 1) + '\t';
};
FilterSingleWildcardLeftAnchored.fromSelfie = function(s) { FilterSingleWildcardLeftAnchored.fromSelfie = function(s) {
var pos = s.indexOf('\t'); var pos = s.indexOf('\t');
return new FilterSingleWildcardLeftAnchored(s.slice(0, pos), s.slice(pos + 1)); return new FilterSingleWildcardLeftAnchored(s.slice(0, pos), s.slice(pos + 1));
@ -687,7 +771,7 @@ FilterSingleWildcardLeftAnchoredHostname.prototype.match = function(url) {
url.indexOf(this.rSegment, this.lSegment.length) > 0; url.indexOf(this.rSegment, this.lSegment.length) > 0;
}; };
FilterSingleWildcardLeftAnchoredHostname.prototype.fid = '|*h'; FilterSingleWildcardLeftAnchoredHostname.fid = FilterSingleWildcardLeftAnchoredHostname.prototype.fid = '|*h';
FilterSingleWildcardLeftAnchoredHostname.prototype.toString = function() { FilterSingleWildcardLeftAnchoredHostname.prototype.toString = function() {
return '|' + this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname; return '|' + this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
@ -699,6 +783,14 @@ FilterSingleWildcardLeftAnchoredHostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterSingleWildcardLeftAnchoredHostname.compile = function(details, hostname) {
var s = details.f;
var pos = s.indexOf('*');
return s.slice(0, pos) + '\t' +
s.slice(pos + 1) + '\t' +
hostname;
};
FilterSingleWildcardLeftAnchoredHostname.fromSelfie = function(s) { FilterSingleWildcardLeftAnchoredHostname.fromSelfie = function(s) {
var args = s.split('\t'); var args = s.split('\t');
return new FilterSingleWildcardLeftAnchoredHostname(args[0], args[1], args[2]); return new FilterSingleWildcardLeftAnchoredHostname(args[0], args[1], args[2]);
@ -716,7 +808,7 @@ FilterSingleWildcardRightAnchored.prototype.match = function(url) {
url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0; url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0;
}; };
FilterSingleWildcardRightAnchored.prototype.fid = '*|'; FilterSingleWildcardRightAnchored.fid = FilterSingleWildcardRightAnchored.prototype.fid = '*|';
FilterSingleWildcardRightAnchored.prototype.toString = function() { FilterSingleWildcardRightAnchored.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment + '|'; return this.lSegment + '*' + this.rSegment + '|';
@ -727,6 +819,13 @@ FilterSingleWildcardRightAnchored.prototype.toSelfie = function() {
this.rSegment; this.rSegment;
}; };
FilterSingleWildcardRightAnchored.compile = function(details) {
var s = details.f;
var pos = s.indexOf('*');
return s.slice(0, pos) + '\t' +
s.slice(pos + 1) + '\t';
};
FilterSingleWildcardRightAnchored.fromSelfie = function(s) { FilterSingleWildcardRightAnchored.fromSelfie = function(s) {
var pos = s.indexOf('\t'); var pos = s.indexOf('\t');
return new FilterSingleWildcardRightAnchored(s.slice(0, pos), s.slice(pos + 1)); return new FilterSingleWildcardRightAnchored(s.slice(0, pos), s.slice(pos + 1));
@ -746,7 +845,7 @@ FilterSingleWildcardRightAnchoredHostname.prototype.match = function(url) {
url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0; url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0;
}; };
FilterSingleWildcardRightAnchoredHostname.prototype.fid = '*|h'; FilterSingleWildcardRightAnchoredHostname.fid = FilterSingleWildcardRightAnchoredHostname.prototype.fid = '*|h';
FilterSingleWildcardRightAnchoredHostname.prototype.toString = function() { FilterSingleWildcardRightAnchoredHostname.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment + '|$domain=' + this.hostname; return this.lSegment + '*' + this.rSegment + '|$domain=' + this.hostname;
@ -758,6 +857,14 @@ FilterSingleWildcardRightAnchoredHostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterSingleWildcardRightAnchoredHostname.compile = function(details, hostname) {
var s = details.f;
var pos = s.indexOf('*');
return s.slice(0, pos) + '\t' +
s.slice(pos + 1) + '\t' +
hostname;
};
FilterSingleWildcardRightAnchoredHostname.fromSelfie = function(s) { FilterSingleWildcardRightAnchoredHostname.fromSelfie = function(s) {
var args = s.split('\t'); var args = s.split('\t');
return new FilterSingleWildcardRightAnchoredHostname(args[0], args[1], args[2]); return new FilterSingleWildcardRightAnchoredHostname(args[0], args[1], args[2]);
@ -781,15 +888,18 @@ FilterManyWildcards.prototype.match = function(url, tokenBeg) {
return this.re.test(url.slice(tokenBeg - this.tokenBeg)); return this.re.test(url.slice(tokenBeg - this.tokenBeg));
}; };
FilterManyWildcards.prototype.fid = '*+'; FilterManyWildcards.fid = FilterManyWildcards.prototype.fid = '*+';
FilterManyWildcards.prototype.toString = function() { FilterManyWildcards.prototype.toString = function() {
return this.s; return this.s;
}; };
FilterManyWildcards.prototype.toSelfie = function() { FilterManyWildcards.prototype.toSelfie = function() {
return this.s + '\t' + return this.s + '\t' + this.tokenBeg;
this.tokenBeg; };
FilterManyWildcards.compile = function(details) {
return details.f + '\t' + details.tokenBeg;
}; };
FilterManyWildcards.fromSelfie = function(s) { FilterManyWildcards.fromSelfie = function(s) {
@ -811,7 +921,7 @@ FilterManyWildcardsHostname.prototype.match = function(url, tokenBeg) {
this.re.test(url.slice(tokenBeg - this.tokenBeg)); this.re.test(url.slice(tokenBeg - this.tokenBeg));
}; };
FilterManyWildcardsHostname.prototype.fid = '*+h'; FilterManyWildcardsHostname.fid = FilterManyWildcardsHostname.prototype.fid = '*+h';
FilterManyWildcardsHostname.prototype.toString = function() { FilterManyWildcardsHostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname; return this.s + '$domain=' + this.hostname;
@ -823,6 +933,12 @@ FilterManyWildcardsHostname.prototype.toSelfie = function() {
this.hostname; this.hostname;
}; };
FilterManyWildcardsHostname.compile = function(details, hostname) {
return details.f + '\t' +
details.tokenBeg + '\t' +
hostname;
};
FilterManyWildcardsHostname.fromSelfie = function(s) { FilterManyWildcardsHostname.fromSelfie = function(s) {
var args = s.split('\t'); var args = s.split('\t');
return new FilterManyWildcardsHostname(args[0], atoi(args[1]), args[2]); return new FilterManyWildcardsHostname(args[0], atoi(args[1]), args[2]);
@ -840,7 +956,7 @@ FilterRegex.prototype.match = function(url) {
return this.re.test(url); return this.re.test(url);
}; };
FilterRegex.prototype.fid = '//'; FilterRegex.fid = FilterRegex.prototype.fid = '//';
FilterRegex.prototype.toString = function() { FilterRegex.prototype.toString = function() {
return '/' + this.re.source + '/'; return '/' + this.re.source + '/';
@ -850,6 +966,10 @@ FilterRegex.prototype.toSelfie = function() {
return this.re.source; return this.re.source;
}; };
FilterRegex.compile = function(details) {
return details.f;
};
FilterRegex.fromSelfie = function(s) { FilterRegex.fromSelfie = function(s) {
return new FilterRegex(s); return new FilterRegex(s);
}; };
@ -867,15 +987,18 @@ FilterRegexHostname.prototype.match = function(url) {
this.re.test(url); this.re.test(url);
}; };
FilterRegexHostname.prototype.fid = '//h'; FilterRegexHostname.fid = FilterRegexHostname.prototype.fid = '//h';
FilterRegexHostname.prototype.toString = function() { FilterRegexHostname.prototype.toString = function() {
return '/' + this.re.source + '/$domain=' + this.hostname; return '/' + this.re.source + '/$domain=' + this.hostname;
}; };
FilterRegexHostname.prototype.toSelfie = function() { FilterRegexHostname.prototype.toSelfie = function() {
return this.re.source + '\t' + return this.re.source + '\t' + this.hostname;
this.hostname; };
FilterRegexHostname.compile = function(details, hostname) {
return details.f + '\t' + hostname;
}; };
FilterRegexHostname.fromSelfie = function(s) { FilterRegexHostname.fromSelfie = function(s) {
@ -981,12 +1104,12 @@ FilterHostnameDict.prototype.add = function(hn) {
if ( typeof bucket === 'string' ) { if ( typeof bucket === 'string' ) {
bucket = this.dict[key] = this.meltBucket(hn.len, bucket); bucket = this.dict[key] = this.meltBucket(hn.len, bucket);
} }
if ( bucket[hn] === undefined ) { if ( bucket.hasOwnProperty(hn) ) {
return false;
}
bucket[hn] = true; bucket[hn] = true;
this.count += 1; this.count += 1;
return true; return true;
}
return false;
}; };
FilterHostnameDict.prototype.freeze = function() { FilterHostnameDict.prototype.freeze = function() {
@ -1041,7 +1164,7 @@ FilterHostnameDict.prototype.matchesExactly = function(hn) {
return false; return false;
}; };
FilterHostnameDict.prototype.match = function(hn) { FilterHostnameDict.prototype.match = function() {
// TODO: mind IP addresses // TODO: mind IP addresses
var pos, var pos,
@ -1058,7 +1181,7 @@ FilterHostnameDict.prototype.match = function(hn) {
return this; return this;
}; };
FilterHostnameDict.prototype.fid = '{h}'; FilterHostnameDict.fid = FilterHostnameDict.prototype.fid = '{h}';
FilterHostnameDict.prototype.toString = function() { FilterHostnameDict.prototype.toString = function() {
return this.h; return this.h;
@ -1186,85 +1309,81 @@ FilterBucket.fromSelfie = function() {
/******************************************************************************/ /******************************************************************************/
var makeFilter = function(details) { var getFilterClass = function(details) {
var s = details.f;
if ( details.isRegex ) { if ( details.isRegex ) {
return new FilterRegex(s); return FilterRegex;
} }
var s = details.f;
var wcOffset = s.indexOf('*'); var wcOffset = s.indexOf('*');
if ( wcOffset !== -1 ) { if ( wcOffset !== -1 ) {
if ( s.indexOf('*', wcOffset + 1) !== -1 ) { if ( s.indexOf('*', wcOffset + 1) !== -1 ) {
return details.anchor === 0 ? new FilterManyWildcards(s, details.tokenBeg) : null; return details.anchor === 0 ? FilterManyWildcards : null;
} }
var lSegment = s.slice(0, wcOffset);
var rSegment = s.slice(wcOffset + 1);
if ( details.anchor < 0 ) { if ( details.anchor < 0 ) {
return new FilterSingleWildcardLeftAnchored(lSegment, rSegment); return FilterSingleWildcardLeftAnchored;
} }
if ( details.anchor > 0 ) { if ( details.anchor > 0 ) {
return new FilterSingleWildcardRightAnchored(lSegment, rSegment); return FilterSingleWildcardRightAnchored;
} }
if ( details.tokenBeg === 0 ) { if ( details.tokenBeg === 0 ) {
return new FilterSingleWildcardPrefix0(lSegment, rSegment); return FilterSingleWildcardPrefix0;
} }
return new FilterSingleWildcard(lSegment, rSegment, details.tokenBeg); return FilterSingleWildcard;
} }
if ( details.anchor < 0 ) { if ( details.anchor < 0 ) {
return new FilterPlainLeftAnchored(s); return FilterPlainLeftAnchored;
} }
if ( details.anchor > 0 ) { if ( details.anchor > 0 ) {
return new FilterPlainRightAnchored(s); return FilterPlainRightAnchored;
} }
if ( details.hostnameAnchored ) { if ( details.hostnameAnchored ) {
return new FilterPlainHnAnchored(s); return FilterPlainHnAnchored;
} }
if ( details.tokenBeg === 0 ) { if ( details.tokenBeg === 0 ) {
return new FilterPlainPrefix0(s); return FilterPlainPrefix0;
} }
if ( details.tokenBeg === 1 ) { if ( details.tokenBeg === 1 ) {
return new FilterPlainPrefix1(s); return FilterPlainPrefix1;
} }
return new FilterPlain(s, details.tokenBeg); return FilterPlain;
}; };
/******************************************************************************/ /******************************************************************************/
var makeHostnameFilter = function(details, hostname) { var getHostnameBasedFilterClass = function(details) {
var s = details.f;
if ( details.isRegex ) { if ( details.isRegex ) {
return new FilterRegexHostname(s, hostname); return FilterRegexHostname;
} }
var s = details.f;
var wcOffset = s.indexOf('*'); var wcOffset = s.indexOf('*');
if ( wcOffset !== -1 ) { if ( wcOffset !== -1 ) {
if ( s.indexOf('*', wcOffset + 1) !== -1 ) { if ( s.indexOf('*', wcOffset + 1) !== -1 ) {
return details.anchor === 0 ? new FilterManyWildcardsHostname(s, details.tokenBeg, hostname) : null; return details.anchor === 0 ? FilterManyWildcardsHostname : null;
}
var lSegment = s.slice(0, wcOffset);
var rSegment = s.slice(wcOffset + 1);
if ( details.anchor < 0 ) {
return new FilterSingleWildcardLeftAnchoredHostname(lSegment, rSegment, hostname);
}
if ( details.anchor > 0 ) {
return new FilterSingleWildcardRightAnchoredHostname(lSegment, rSegment, hostname);
}
if ( details.tokenBeg === 0 ) {
return new FilterSingleWildcardPrefix0Hostname(lSegment, rSegment, hostname);
}
return new FilterSingleWildcardHostname(lSegment, rSegment, details.tokenBeg, hostname);
} }
if ( details.anchor < 0 ) { if ( details.anchor < 0 ) {
return new FilterPlainLeftAnchoredHostname(s, hostname); return FilterSingleWildcardLeftAnchoredHostname;
} }
if ( details.anchor > 0 ) { if ( details.anchor > 0 ) {
return new FilterPlainRightAnchoredHostname(s, hostname); return FilterSingleWildcardRightAnchoredHostname;
} }
if ( details.tokenBeg === 0 ) { if ( details.tokenBeg === 0 ) {
return new FilterPlainPrefix0Hostname(s, hostname); return FilterSingleWildcardPrefix0Hostname;
}
return FilterSingleWildcardHostname;
}
if ( details.anchor < 0 ) {
return FilterPlainLeftAnchoredHostname;
}
if ( details.anchor > 0 ) {
return FilterPlainRightAnchoredHostname;
}
if ( details.tokenBeg === 0 ) {
return FilterPlainPrefix0Hostname;
} }
if ( details.tokenBeg === 1 ) { if ( details.tokenBeg === 1 ) {
return new FilterPlainPrefix1Hostname(s, hostname); return FilterPlainPrefix1Hostname;
} }
return new FilterPlainHostname(s, details.tokenBeg, hostname); return FilterPlainHostname;
}; };
/******************************************************************************/ /******************************************************************************/
@ -1615,8 +1734,8 @@ FilterContainer.prototype.reset = function() {
this.allowFilterCount = 0; this.allowFilterCount = 0;
this.blockFilterCount = 0; this.blockFilterCount = 0;
this.duplicateCount = 0; this.duplicateCount = 0;
this.duplicateBuster = {};
this.categories = Object.create(null); this.categories = Object.create(null);
this.duplicates = Object.create(null);
this.filterParser.reset(); this.filterParser.reset();
}; };
@ -1624,6 +1743,8 @@ FilterContainer.prototype.reset = function() {
FilterContainer.prototype.freeze = function() { FilterContainer.prototype.freeze = function() {
histogram('allFilters', this.categories); histogram('allFilters', this.categories);
this.duplicateBuster = {};
var categories = this.categories; var categories = this.categories;
var bucket; var bucket;
for ( var k in categories ) { for ( var k in categories ) {
@ -1632,13 +1753,43 @@ FilterContainer.prototype.freeze = function() {
bucket.freeze(); bucket.freeze();
} }
} }
this.duplicates = Object.create(null);
this.filterParser.reset(); this.filterParser.reset();
this.frozen = true; this.frozen = true;
}; };
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.factories = {
'[]': FilterBucket,
'a': FilterPlain,
'ah': FilterPlainHostname,
'0a': FilterPlainPrefix0,
'0ah': FilterPlainPrefix0Hostname,
'1a': FilterPlainPrefix1,
'1ah': FilterPlainPrefix1Hostname,
'|a': FilterPlainLeftAnchored,
'|ah': FilterPlainLeftAnchoredHostname,
'a|': FilterPlainRightAnchored,
'a|h': FilterPlainRightAnchoredHostname,
'h|a': FilterPlainHnAnchored,
'*': FilterSingleWildcard,
'*h': FilterSingleWildcardHostname,
'0*': FilterSingleWildcardPrefix0,
'0*h': FilterSingleWildcardPrefix0Hostname,
'|*': FilterSingleWildcardLeftAnchored,
'|*h': FilterSingleWildcardLeftAnchoredHostname,
'*|': FilterSingleWildcardRightAnchored,
'*|h': FilterSingleWildcardRightAnchoredHostname,
'*+': FilterManyWildcards,
'*+h': FilterManyWildcardsHostname,
'//': FilterRegex,
'//h': FilterRegexHostname,
'{h}': FilterHostnameDict
};
/******************************************************************************/
FilterContainer.prototype.toSelfie = function() { FilterContainer.prototype.toSelfie = function() {
var categoryToSelfie = function(dict) { var categoryToSelfie = function(dict) {
var selfie = []; var selfie = [];
@ -1697,34 +1848,6 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
this.blockFilterCount = selfie.blockFilterCount; this.blockFilterCount = selfie.blockFilterCount;
this.duplicateCount = selfie.duplicateCount; this.duplicateCount = selfie.duplicateCount;
var factories = {
'[]': FilterBucket,
'a': FilterPlain,
'ah': FilterPlainHostname,
'0a': FilterPlainPrefix0,
'0ah': FilterPlainPrefix0Hostname,
'1a': FilterPlainPrefix1,
'1ah': FilterPlainPrefix1Hostname,
'|a': FilterPlainLeftAnchored,
'|ah': FilterPlainLeftAnchoredHostname,
'a|': FilterPlainRightAnchored,
'a|h': FilterPlainRightAnchoredHostname,
'h|a': FilterPlainHnAnchored,
'*': FilterSingleWildcard,
'*h': FilterSingleWildcardHostname,
'0*': FilterSingleWildcardPrefix0,
'0*h': FilterSingleWildcardPrefix0Hostname,
'|*': FilterSingleWildcardLeftAnchored,
'|*h': FilterSingleWildcardLeftAnchoredHostname,
'*|': FilterSingleWildcardRightAnchored,
'*|h': FilterSingleWildcardRightAnchoredHostname,
'*+': FilterManyWildcards,
'*+h': FilterManyWildcardsHostname,
'//': FilterRegex,
'//h': FilterRegexHostname,
'{h}': FilterHostnameDict
};
var catKey, tokenKey; var catKey, tokenKey;
var dict = this.categories, subdict; var dict = this.categories, subdict;
var bucket = null; var bucket = null;
@ -1752,7 +1875,7 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
bucket = null; bucket = null;
continue; continue;
} }
factory = factories[what]; factory = this.factories[what];
if ( bucket === null ) { if ( bucket === null ) {
bucket = subdict[tokenKey] = factory.fromSelfie(line.slice(pos + 1)); bucket = subdict[tokenKey] = factory.fromSelfie(line.slice(pos + 1));
continue; continue;
@ -1766,12 +1889,12 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.makeCategoryKey = function(category) { FilterContainer.prototype.makeCategoryKey = function(category) {
return String.fromCharCode(category); return category.toString(16);
}; };
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.add = function(raw) { FilterContainer.prototype.compile = function(raw, out) {
// ORDER OF TESTS IS IMPORTANT! // ORDER OF TESTS IS IMPORTANT!
// Ignore empty lines // Ignore empty lines
@ -1795,40 +1918,22 @@ FilterContainer.prototype.add = function(raw) {
// Ignore filters with unsupported options // Ignore filters with unsupported options
if ( parsed.unsupported ) { if ( parsed.unsupported ) {
this.rejectedCount += 1;
//console.log('static-net-filtering.js > FilterContainer.add(): unsupported filter "%s"', raw); //console.log('static-net-filtering.js > FilterContainer.add(): unsupported filter "%s"', raw);
return false; return false;
} }
this.processedFilterCount += 1;
this.acceptedCount += 1;
// Pure hostnames, use more efficient liquid dict // Pure hostnames, use more efficient liquid dict
// https://github.com/gorhill/uBlock/issues/665 // https://github.com/gorhill/uBlock/issues/665
// Create a dict keyed on request type etc. // Create a dict keyed on request type etc.
if ( parsed.hostnamePure && this.addHostnameOnlyFilter(parsed) ) { if ( parsed.hostnamePure && this.compileHostnameOnlyFilter(parsed, out) ) {
return true; return true;
} }
if ( this.duplicates[s] ) { var r = this.compileFilter(parsed, out);
//console.log('static-net-filtering.js > FilterContainer.add(): duplicate filter "%s"', raw);
this.duplicateCount++;
return false;
}
if ( this.frozen === false ) {
this.duplicates[s] = true;
}
var r = this.addFilter(parsed);
if ( r === false ) { if ( r === false ) {
return false; return false;
} }
if ( parsed.action ) {
this.allowFilterCount += 1;
} else {
this.blockFilterCount += 1;
}
return true; return true;
}; };
@ -1836,55 +1941,37 @@ FilterContainer.prototype.add = function(raw) {
// Using fast/compact dictionary when filter is a (or portion of) pure hostname. // Using fast/compact dictionary when filter is a (or portion of) pure hostname.
FilterContainer.prototype.addHostnameOnlyFilter = function(parsed) { FilterContainer.prototype.compileHostnameOnlyFilter = function(parsed, out) {
// Can't fit the filter in a pure hostname dictionary. // Can't fit the filter in a pure hostname dictionary.
if ( parsed.hostnames.length !== 0 || parsed.notHostnames.length !== 0 ) { if ( parsed.hostnames.length !== 0 || parsed.notHostnames.length !== 0 ) {
return false; return;
} }
var isNewFilter = false;
var party = AnyParty; var party = AnyParty;
if ( parsed.firstParty !== parsed.thirdParty ) { if ( parsed.firstParty !== parsed.thirdParty ) {
party = parsed.firstParty ? FirstParty : ThirdParty; party = parsed.firstParty ? FirstParty : ThirdParty;
} }
var keyShard = parsed.action | parsed.important | party; var keyShard = parsed.action | parsed.important | party;
var key, bucket;
var type = parsed.types >>> 1 || 1; // bit 0 is unused; also, default to AnyType var type = parsed.types >>> 1 || 1; // bit 0 is unused; also, default to AnyType
var bitOffset = 1; var bitOffset = 1;
while ( type !== 0 ) { while ( type !== 0 ) {
if ( type & 1 ) { if ( type & 1 ) {
key = this.makeCategoryKey(keyShard | (bitOffset << 4)); out.push(
bucket = this.categories[key]; 'n\v' +
if ( bucket === undefined ) { this.makeCategoryKey(keyShard | (bitOffset << 4)) + '\v' +
bucket = this.categories[key] = Object.create(null); '.\v' +
} parsed.f
if ( bucket['.'] === undefined ) { );
bucket['.'] = new FilterHostnameDict();
}
if ( bucket['.'].add(parsed.f) ) {
isNewFilter = true;
}
} }
bitOffset += 1; bitOffset += 1;
type >>>= 1; type >>>= 1;
} }
// https://github.com/gorhill/uBlock/issues/719
// Count whole filter, not its decomposed versions
if ( isNewFilter ) {
if ( parsed.action ) {
this.allowFilterCount += 1;
} else {
this.blockFilterCount += 1;
}
} else {
this.duplicateCount += 1;
}
return true; return true;
}; };
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.addFilter = function(parsed) { FilterContainer.prototype.compileFilter = function(parsed, out) {
parsed.makeToken(); parsed.makeToken();
if ( parsed.token === '' ) { if ( parsed.token === '' ) {
console.error('static-net-filtering.js > FilterContainer.addFilter("%s"): can\'t tokenize', parsed.f); console.error('static-net-filtering.js > FilterContainer.addFilter("%s"): can\'t tokenize', parsed.f);
@ -1896,28 +1983,28 @@ FilterContainer.prototype.addFilter = function(parsed) {
party = parsed.firstParty ? FirstParty : ThirdParty; party = parsed.firstParty ? FirstParty : ThirdParty;
} }
var filter; var filterClass;
var i = parsed.hostnames.length; var i = parsed.hostnames.length;
var j = parsed.notHostnames.length; var j = parsed.notHostnames.length;
// Applies to all domains without exceptions // Applies to all domains without exceptions
if ( i === 0 && j === 0 ) { if ( i === 0 && j === 0 ) {
filter = makeFilter(parsed); filterClass = getFilterClass(parsed);
if ( !filter ) { if ( filterClass === null ) {
return false; return false;
} }
this.addFilterEntry(filter, parsed, party); this.compileToAtomicFilter(filterClass, parsed, party, out);
return true; return true;
} }
// Applies to specific domains // Applies to specific domains
if ( i !== 0 ) { if ( i !== 0 ) {
while ( i-- ) { while ( i-- ) {
filter = makeHostnameFilter(parsed, parsed.hostnames[i]); filterClass = getHostnameBasedFilterClass(parsed);
if ( !filter ) { if ( filterClass === null ) {
return false; return false;
} }
this.addFilterEntry(filter, parsed, party); this.compileToAtomicFilter(filterClass, parsed, party, out, parsed.hostnames[i]);
} }
} }
// No exceptions // No exceptions
@ -1930,13 +2017,13 @@ FilterContainer.prototype.addFilter = function(parsed) {
// Example: // Example:
// - ||adm.fwmrm.net/p/msnbc_live/$object-subrequest,third-party,domain=~msnbc.msn.com|~www.nbcnews.com // - ||adm.fwmrm.net/p/msnbc_live/$object-subrequest,third-party,domain=~msnbc.msn.com|~www.nbcnews.com
if ( i === 0 ) { if ( i === 0 ) {
filter = makeFilter(parsed); filterClass = getFilterClass(parsed);
if ( !filter ) { if ( filterClass === null ) {
return false; return false;
} }
// https://github.com/gorhill/uBlock/issues/251 // https://github.com/gorhill/uBlock/issues/251
// Apply third-party option if it is present // Apply third-party option if it is present
this.addFilterEntry(filter, parsed, party); this.compileToAtomicFilter(filterClass, parsed, party, out);
} }
// Cases: // Cases:
@ -1948,8 +2035,8 @@ FilterContainer.prototype.addFilter = function(parsed) {
// Reverse purpose of filter // Reverse purpose of filter
parsed.action ^= ToggleAction; parsed.action ^= ToggleAction;
while ( j-- ) { while ( j-- ) {
filter = makeHostnameFilter(parsed, parsed.notHostnames[j]); filterClass = getHostnameBasedFilterClass(parsed);
if ( !filter ) { if ( filterClass === null ) {
return false; return false;
} }
// https://github.com/gorhill/uBlock/issues/191#issuecomment-53654024 // https://github.com/gorhill/uBlock/issues/191#issuecomment-53654024
@ -1958,20 +2045,26 @@ FilterContainer.prototype.addFilter = function(parsed) {
if ( parsed.action === BlockAction ) { if ( parsed.action === BlockAction ) {
parsed.important = Important; parsed.important = Important;
} }
this.addFilterEntry(filter, parsed, party); this.compileToAtomicFilter(filterClass, parsed, party, out, parsed.notHostnames[j]);
} }
return true; return true;
}; };
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.addFilterEntry = function(filter, parsed, party) { FilterContainer.prototype.compileToAtomicFilter = function(filterClass, parsed, party, out, hostname) {
var bits = parsed.action | parsed.important | party; var bits = parsed.action | parsed.important | party;
var type = parsed.types >>> 1 || 1; // bit 0 is unused; also, default to AnyType var type = parsed.types >>> 1 || 1; // bit 0 is unused; also, default to AnyType
var bitOffset = 1; var bitOffset = 1;
while ( type !== 0 ) { while ( type !== 0 ) {
if ( type & 1 ) { if ( type & 1 ) {
this.addToCategory(bits | (bitOffset << 4), parsed.token, filter); out.push(
'n\v' +
this.makeCategoryKey(bits | (bitOffset << 4)) + '\v' +
parsed.token + '\v' +
filterClass.fid + '\v' +
filterClass.compile(parsed, hostname)
);
} }
bitOffset += 1; bitOffset += 1;
type >>>= 1; type >>>= 1;
@ -1980,22 +2073,60 @@ FilterContainer.prototype.addFilterEntry = function(filter, parsed, party) {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.addToCategory = function(category, tokenKey, filter) { FilterContainer.prototype.fromCompiledContent = function(text, lineBeg) {
var categoryKey = this.makeCategoryKey(category); var lineEnd;
var categoryBucket = this.categories[categoryKey]; var textEnd = text.length;
if ( !categoryBucket ) { var line, fields, bucket, entry, factory, filter;
categoryBucket = this.categories[categoryKey] = Object.create(null);
while ( lineBeg < textEnd ) {
if ( text.charAt(lineBeg) !== 'n' ) {
return lineBeg;
} }
var filterEntry = categoryBucket[tokenKey]; lineEnd = text.indexOf('\n', lineBeg);
if ( filterEntry === undefined ) { if ( lineEnd === -1 ) {
categoryBucket[tokenKey] = filter; lineEnd = textEnd;
return;
} }
if ( filterEntry.fid === '[]' ) { line = text.slice(lineBeg + 2, lineEnd);
filterEntry.add(filter); fields = line.split('\v');
return; lineBeg = lineEnd + 1;
this.acceptedCount += 1;
bucket = this.categories[fields[0]];
if ( bucket === undefined ) {
bucket = this.categories[fields[0]] = Object.create(null);
} }
categoryBucket[tokenKey] = new FilterBucket(filterEntry, filter); entry = bucket[fields[1]];
if ( fields[1] === '.' ) {
if ( entry === undefined ) {
entry = bucket['.'] = new FilterHostnameDict();
}
if ( entry.add(fields[2]) === false ) {
this.duplicateCount += 1;
}
continue;
}
if ( this.duplicateBuster.hasOwnProperty(line) ) {
this.duplicateCount += 1;
continue;
}
this.duplicateBuster[line] = true;
factory = this.factories[fields[2]];
filter = factory.fromSelfie(fields[3]);
if ( entry === undefined ) {
bucket[fields[1]] = filter;
continue;
}
if ( entry.fid === '[]' ) {
entry.add(filter);
continue;
}
bucket[fields[1]] = new FilterBucket(entry, filter);
}
return textEnd;
}; };
/******************************************************************************/ /******************************************************************************/
@ -2275,7 +2406,7 @@ FilterContainer.prototype.matchString = function(context) {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.getFilterCount = function() { FilterContainer.prototype.getFilterCount = function() {
return this.blockFilterCount + this.allowFilterCount; return this.acceptedCount - this.duplicateCount;
}; };
/******************************************************************************/ /******************************************************************************/

View File

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* global µBlock, vAPI, punycode, publicSuffixList */ /* global YaMD5, µBlock, vAPI, punycode, publicSuffixList */
'use strict'; 'use strict';
@ -53,6 +53,21 @@
/******************************************************************************/ /******************************************************************************/
µBlock.saveSystemSettings = function() {
vAPI.storage.set(this.systemSettings, this.noopFunc);
};
/******************************************************************************/
µBlock.loadSystemSettings = function(callback) {
vAPI.storage.get({
compiledMagic: '',
selfieMagic: ''
}, callback);
};
/******************************************************************************/
// Save local settings regularly. Not critical. // Save local settings regularly. Not critical.
µBlock.asyncJobs.add( µBlock.asyncJobs.add(
@ -133,17 +148,30 @@
/******************************************************************************/ /******************************************************************************/
µBlock.appendUserFilters = function(content) { µBlock.appendUserFilters = function(content) {
if ( content.length === 0 ) {
return;
}
var µb = this; var µb = this;
var onCompiledListLoaded = function(details) {
var snfe = µb.staticNetFilteringEngine;
var cfe = µb.cosmeticFilteringEngine;
var acceptedCount = snfe.acceptedCount + cfe.acceptedCount;
var duplicateCount = snfe.duplicateCount + cfe.duplicateCount;
µb.applyCompiledFilters(details.content);
var entry = µb.remoteBlacklists[µb.userFiltersPath];
entry.entryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount;
entry.entryUsedCount = entry.entryCount - snfe.duplicateCount - cfe.duplicateCount + duplicateCount;
µb.staticNetFilteringEngine.freeze();
µb.cosmeticFilteringEngine.freeze();
};
var onSaved = function(details) { var onSaved = function(details) {
if ( details.error ) { if ( details.error ) {
return; return;
} }
µb.mergeFilterText(content); µb.getCompiledFilterList(µb.userFiltersPath, onCompiledListLoaded);
µb.staticNetFilteringEngine.freeze();
µb.cosmeticFilteringEngine.freeze();
µb.destroySelfie();
µb.toSelfieAsync();
}; };
var onLoaded = function(details) { var onLoaded = function(details) {
@ -153,12 +181,10 @@
if ( details.content.indexOf(content.trim()) !== -1 ) { if ( details.content.indexOf(content.trim()) !== -1 ) {
return; return;
} }
µb.saveUserFilters(details.content + '\n' + content, onSaved); µb.saveUserFilters(details.content.trim() + '\n' + content.trim(), onSaved);
}; };
if ( content.length > 0 ) {
this.loadUserFilters(onLoaded); this.loadUserFilters(onLoaded);
}
}; };
/******************************************************************************/ /******************************************************************************/
@ -260,111 +286,152 @@
/******************************************************************************/ /******************************************************************************/
µBlock.createShortUniqueId = function(path) {
var md5 = YaMD5.hashStr(path);
return md5.slice(0, 4) + md5.slice(-4);
};
µBlock.createShortUniqueId.idLength = 8;
/******************************************************************************/
µBlock.loadFilterLists = function(callback) { µBlock.loadFilterLists = function(callback) {
//quickProfiler.start('µBlock.loadFilterLists()');
var µb = this; var µb = this;
var filterlistCount; var filterlistsCount = 0;
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = this.noopFunc; callback = this.noopFunc;
} }
var loadBlacklistsEnd = function() { var onDone = function() {
µb.staticNetFilteringEngine.freeze(); µb.staticNetFilteringEngine.freeze();
µb.cosmeticFilteringEngine.freeze(); µb.cosmeticFilteringEngine.freeze();
vAPI.storage.set({ 'remoteBlacklists': µb.remoteBlacklists }); vAPI.storage.set({ 'remoteBlacklists': µb.remoteBlacklists });
vAPI.messaging.broadcast({ what: 'loadUbiquitousBlacklistCompleted' }); vAPI.messaging.broadcast({ what: 'allFilterListsReloaded' });
µb.toSelfieAsync();
callback(); callback();
µb.toSelfieAsync();
//quickProfiler.stop(0);
}; };
var mergeBlacklist = function(details) { var applyCompiledFilters = function(path, compiled) {
µb.mergeFilterList(details); var snfe = µb.staticNetFilteringEngine;
filterlistCount -= 1; var cfe = µb.cosmeticFilteringEngine;
if ( filterlistCount === 0 ) { var acceptedCount = snfe.acceptedCount + cfe.acceptedCount;
loadBlacklistsEnd(); var duplicateCount = snfe.duplicateCount + cfe.duplicateCount;
µb.applyCompiledFilters(compiled);
if ( µb.remoteBlacklists.hasOwnProperty(path) ) {
var entry = µb.remoteBlacklists[path];
entry.entryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount;
entry.entryUsedCount = entry.entryCount - snfe.duplicateCount - cfe.duplicateCount + duplicateCount;
} }
}; };
var loadBlacklistsStart = function(lists) { var onCompiledListLoaded = function(details) {
µb.remoteBlacklists = lists; applyCompiledFilters(details.path, details.content);
µb.staticNetFilteringEngine.reset(); filterlistsCount -= 1;
µb.cosmeticFilteringEngine.reset(); if ( filterlistsCount === 0 ) {
µb.destroySelfie(); onDone();
var locations = Object.keys(lists); }
filterlistCount = locations.length; };
// Load all filter lists which are not disabled var onFilterListsReady = function(lists) {
var location; µb.remoteBlacklists = lists;
while ( location = locations.pop() ) {
// rhill 2013-12-09: µb.cosmeticFilteringEngine.reset();
// Ignore list if disabled µb.staticNetFilteringEngine.reset();
// https://github.com/gorhill/httpswitchboard/issues/78 µb.destroySelfie();
if ( lists[location].off ) {
filterlistCount -= 1; // We need to build a complete list of assets to pull first: this is
// because it *may* happens that some load operations are synchronous:
// This happens for assets which do not exist, ot assets with no
// content.
var toLoad = [];
for ( var path in lists ) {
if ( lists.hasOwnProperty(path) === false ) {
continue; continue;
} }
µb.assets.get(location, mergeBlacklist); if ( lists[path].off ) {
continue;
} }
// https://github.com/gorhill/uBlock/issues/695 toLoad.push(path);
// It may happen not a single filter list is selected }
if ( filterlistCount === 0 ) { filterlistsCount = toLoad.length;
loadBlacklistsEnd(); if ( filterlistsCount === 0 ) {
onDone();
return;
}
var i = toLoad.length;
while ( i-- ) {
µb.getCompiledFilterList(toLoad[i], onCompiledListLoaded);
} }
}; };
this.getAvailableLists(loadBlacklistsStart); this.getAvailableLists(onFilterListsReady);
}; };
/******************************************************************************/ /******************************************************************************/
µBlock.mergeFilterList = function(details) { µBlock.getCompiledFilterListPath = function(path) {
// console.log('µBlock > mergeFilterList from "%s": "%s..."', details.path, details.content.slice(0, 40)); return 'cache://compiled-filter-list:' + this.createShortUniqueId(path);
var staticNetFilteringEngine = this.staticNetFilteringEngine;
var cosmeticFilteringEngine = this.cosmeticFilteringEngine;
var duplicateCount = staticNetFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount;
var acceptedCount = staticNetFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount;
this.mergeFilterText(details.content);
// For convenience, store the number of entries for this
// blacklist, user might be happy to know this information.
duplicateCount = staticNetFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount - duplicateCount;
acceptedCount = staticNetFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount - acceptedCount;
var filterListMeta = this.remoteBlacklists[details.path];
filterListMeta.entryCount = acceptedCount;
filterListMeta.entryUsedCount = acceptedCount - duplicateCount;
// Try to extract a human-friendly name (works only for
// ABP-compatible filter lists)
if ( filterListMeta.title === '' ) {
var matches = details.content.slice(0, 1024).match(/(?:^|\n)!\s*Title:([^\n]+)/i);
if ( matches !== null ) {
filterListMeta.title = matches[1].trim();
}
}
}; };
/******************************************************************************/ /******************************************************************************/
µBlock.mergeFilterText = function(rawText) { µBlock.getCompiledFilterList = function(path, callback) {
var compiledPath = this.getCompiledFilterListPath(path);
var µb = this;
var onRawListLoaded = function(details) {
if ( details.content !== '' ) {
//console.debug('µBlock.getCompiledFilterList/onRawListLoaded: compiling "%s"', path);
details.content = µb.compileFilters(details.content);
µb.assets.put(compiledPath, details.content);
}
callback(details);
};
var onCompiledListLoaded = function(details) {
if ( details.content === '' ) {
//console.debug('µBlock.getCompiledFilterList/onCompiledListLoaded: no compiled version for "%s"', path);
µb.assets.get(path, onRawListLoaded);
return;
}
//console.debug('µBlock.getCompiledFilterList/onCompiledListLoaded: using compiled version for "%s"', path);
details.path = path;
callback(details);
};
this.assets.get(compiledPath, onCompiledListLoaded);
};
/******************************************************************************/
µBlock.purgeCompiledFilterList = function(path) {
this.assets.purge(this.getCompiledFilterListPath(path));
};
/******************************************************************************/
µBlock.compileFilters = function(rawText) {
var rawEnd = rawText.length; var rawEnd = rawText.length;
var compiledFilters = [];
// Useful references: // Useful references:
// https://adblockplus.org/en/filter-cheatsheet // https://adblockplus.org/en/filter-cheatsheet
// https://adblockplus.org/en/filters // https://adblockplus.org/en/filters
var staticNetFilteringEngine = this.staticNetFilteringEngine; var staticNetFilteringEngine = this.staticNetFilteringEngine;
var cosmeticFilteringEngine = this.cosmeticFilteringEngine; var cosmeticFilteringEngine = this.cosmeticFilteringEngine;
var parseCosmeticFilters = this.userSettings.parseAllABPHideFilters;
var reIsCosmeticFilter = /#[@#]/;
var reIsWhitespaceChar = /\s/; var reIsWhitespaceChar = /\s/;
var reMaybeLocalIp = /^[\d:f]/; var reMaybeLocalIp = /^[\d:f]/;
var reIsLocalhostRedirect = /\s+(?:broadcasthost|local|localhost|localhost\.localdomain)(?=\s|$)/; var reIsLocalhostRedirect = /\s+(?:broadcasthost|local|localhost|localhost\.localdomain)(?=\s|$)/;
var reLocalIp = /^(?:0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)/; var reLocalIp = /^(?:0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)/;
//var reAsciiSegment = /^[\x21-\x7e]+$/;
var lineBeg = 0, lineEnd, currentLineBeg; var lineBeg = 0, lineEnd, currentLineBeg;
var line, lineRaw, c, pos; var line, lineRaw, c, pos;
@ -396,11 +463,7 @@
// Parse or skip cosmetic filters // Parse or skip cosmetic filters
// All cosmetic filters are caught here // All cosmetic filters are caught here
if ( parseCosmeticFilters ) { if ( cosmeticFilteringEngine.compile(line, compiledFilters) ) {
if ( cosmeticFilteringEngine.add(line) ) {
continue;
}
} else if ( reIsCosmeticFilter.test(line) ) {
continue; continue;
} }
@ -440,41 +503,46 @@
continue; continue;
} }
// The filter is whatever sequence of printable ascii character without //staticNetFilteringEngine.add(line);
// whitespaces staticNetFilteringEngine.compile(line, compiledFilters);
//matches = reAsciiSegment.exec(line); }
//if ( matches === null ) {
// console.debug('storage.js > µBlock.mergeFilterList(): skipping "%s"', lineRaw);
// continue;
//}
// Bypass anomalies return compiledFilters.join('\n');
// For example, when a filter contains whitespace characters, or };
// whatever else outside the range of printable ascii characters.
//if ( matches[0] !== line ) {
// console.error('"%s" !== "%s"', matches[0], line);
// continue;
//}
staticNetFilteringEngine.add(line); /******************************************************************************/
µBlock.applyCompiledFilters = function(rawText) {
var skipCosmetic = !this.userSettings.parseAllABPHideFilters;
var staticNetFilteringEngine = this.staticNetFilteringEngine;
var cosmeticFilteringEngine = this.cosmeticFilteringEngine;
var lineBeg = 0;
var rawEnd = rawText.length;
while ( lineBeg < rawEnd ) {
lineBeg = cosmeticFilteringEngine.fromCompiledContent(rawText, lineBeg, skipCosmetic);
lineBeg = staticNetFilteringEngine.fromCompiledContent(rawText, lineBeg);
} }
}; };
/******************************************************************************/ /******************************************************************************/
// `switches` contains the preset blacklists for which the switch must be // `switches` contains the filter lists for which the switch must be revisited.
// revisited.
µBlock.reloadPresetBlacklists = function(switches, update) { µBlock.reloadFilterLists = function(switches, update) {
var µb = µBlock; var µb = this;
var onFilterListsReady = function() { var onFilterListsReady = function() {
µb.loadUpdatableAssets({ update: update, psl: update }); µb.loadUpdatableAssets({ update: update, psl: update });
}; };
var onPurgeDone = function() {
// Toggle switches, if any // Toggle switches, if any
if ( switches !== undefined ) { if ( switches === undefined ) {
var filterLists = this.remoteBlacklists; onFilterListsReady();
return;
}
var filterLists = µb.remoteBlacklists;
var i = switches.length; var i = switches.length;
while ( i-- ) { while ( i-- ) {
if ( filterLists.hasOwnProperty(switches[i].location) === false ) { if ( filterLists.hasOwnProperty(switches[i].location) === false ) {
@ -484,26 +552,76 @@
} }
// Save switch states // Save switch states
vAPI.storage.set({ 'remoteBlacklists': filterLists }, onFilterListsReady); vAPI.storage.set({ 'remoteBlacklists': filterLists }, onFilterListsReady);
} else { };
onFilterListsReady();
// If we must update, we need to purge the compiled versions of
// obsolete assets.
if ( update !== true ) {
onPurgeDone();
return;
} }
var onMetadataReady = function(metadata) {
var filterLists = µb.remoteBlacklists;
var entry;
// Purge obsolete filter lists
for ( var path in filterLists ) {
if ( filterLists.hasOwnProperty(path) === false ) {
continue;
}
if ( metadata.hasOwnProperty(path) === false ) {
continue;
}
entry = metadata[path];
if ( entry.repoObsolete !== true && entry.cacheObsolete !== true ) {
continue;
}
µb.purgeCompiledFilterList(path);
}
// Purge obsolete PSL
if ( metadata.hasOwnProperty(µb.pslPath) === false ) {
entry = metadata[µb.pslPath];
if ( entry.repoObsolete === true ) {
µb.assets.purge('cache://compiled-publicsuffixlist');
}
}
onPurgeDone();
};
this.assets.metadata(onMetadataReady);
}; };
/******************************************************************************/ /******************************************************************************/
µBlock.loadPublicSuffixList = function(callback) { µBlock.loadPublicSuffixList = function(callback) {
var µb = this;
var path = µb.pslPath;
var compiledPath = 'cache://compiled-publicsuffixlist';
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
callback = this.noopFunc; callback = this.noopFunc;
} }
var applyPublicSuffixList = function(details) { var onRawListLoaded = function(details) {
// TODO: Not getting proper suffix list is a bit serious, I think if ( details.content !== '' ) {
// the extension should be force-restarted if it occurs.. //console.debug('µBlock.loadPublicSuffixList/onRawListLoaded: compiling "%s"', path);
if ( !details.error ) {
publicSuffixList.parse(details.content, punycode.toASCII); publicSuffixList.parse(details.content, punycode.toASCII);
µb.assets.put(compiledPath, JSON.stringify(publicSuffixList.toSelfie()));
} }
callback(); callback();
}; };
this.assets.get(this.pslPath, applyPublicSuffixList);
var onCompiledListLoaded = function(details) {
if ( details.content === '' ) {
//console.debug('µBlock.loadPublicSuffixList/onCompiledListLoaded: no compiled version for "%s"', path);
µb.assets.get(path, onRawListLoaded);
return;
}
//console.debug('µBlock.loadPublicSuffixList/onCompiledListLoaded: using compiled version for "%s"', path);
publicSuffixList.fromSelfie(JSON.parse(details.content));
callback();
};
this.assets.get(compiledPath, onCompiledListLoaded);
}; };
/******************************************************************************/ /******************************************************************************/
@ -540,7 +658,7 @@
µBlock.toSelfie = function() { µBlock.toSelfie = function() {
var selfie = { var selfie = {
magic: this.selfieMagic, magic: this.systemSettings.selfieMagic,
publicSuffixList: publicSuffixList.toSelfie(), publicSuffixList: publicSuffixList.toSelfie(),
filterLists: this.remoteBlacklists, filterLists: this.remoteBlacklists,
staticNetFilteringEngine: this.staticNetFilteringEngine.toSelfie(), staticNetFilteringEngine: this.staticNetFilteringEngine.toSelfie(),
@ -578,7 +696,7 @@
var onSelfieReady = function(store) { var onSelfieReady = function(store) {
var selfie = store.selfie; var selfie = store.selfie;
if ( typeof selfie !== 'object' || selfie.magic !== µb.selfieMagic ) { if ( typeof selfie !== 'object' || selfie.magic !== µb.systemSettings.selfieMagic ) {
callback(false); callback(false);
return; return;
} }
@ -586,7 +704,7 @@
callback(false); callback(false);
return; return;
} }
// console.log('µBlock.fromSelfie> selfie looks good'); //console.log('µBlock.fromSelfie> selfie looks good');
µb.remoteBlacklists = selfie.filterLists; µb.remoteBlacklists = selfie.filterLists;
µb.staticNetFilteringEngine.fromSelfie(selfie.staticNetFilteringEngine); µb.staticNetFilteringEngine.fromSelfie(selfie.staticNetFilteringEngine);
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmeticFilteringEngine); µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmeticFilteringEngine);
@ -601,7 +719,7 @@
µBlock.destroySelfie = function() { µBlock.destroySelfie = function() {
vAPI.storage.remove('selfie'); vAPI.storage.remove('selfie');
this.asyncJobs.remove('toSelfie'); this.asyncJobs.remove('toSelfie');
//console.debug('storage.js > µBlock.destroySelfie()'); //console.debug('µBlock.destroySelfie()');
}; };
/******************************************************************************/ /******************************************************************************/
@ -626,6 +744,25 @@
/******************************************************************************/ /******************************************************************************/
µBlock.assetUpdatedHandler = function(details) {
var path = details.path || '';
if ( this.remoteBlacklists.hasOwnProperty(path) === false ) {
return;
}
var entry = this.remoteBlacklists[path];
if ( entry.off ) {
return;
}
// Compile the list while we have the raw version in memory
//console.debug('µBlock.getCompiledFilterList/onRawListLoaded: compiling "%s"', path);
this.assets.put(
this.getCompiledFilterListPath(path),
this.compileFilters(details.content)
);
};
/******************************************************************************/
µBlock.updateCompleteHandler = function(details) { µBlock.updateCompleteHandler = function(details) {
var µb = this; var µb = this;
var updatedCount = details.updatedCount; var updatedCount = details.updatedCount;
@ -659,3 +796,35 @@
onPSLReady(); onPSLReady();
} }
}; };
/******************************************************************************/
µBlock.assetCacheRemovedHandler = (function() {
var barrier = false;
var handler = function(paths) {
if ( barrier ) {
return;
}
barrier = true;
var i = paths.length;
var path;
while ( i-- ) {
path = paths[i];
if ( this.remoteBlacklists.hasOwnProperty(path) ) {
//console.debug('µBlock.assetCacheRemovedHandler: decompiling "%s"', path);
this.purgeCompiledFilterList(path);
continue;
}
if ( path === this.pslPath ) {
//console.debug('µBlock.assetCacheRemovedHandler: decompiling "%s"', path);
this.assets.purge('cache://compiled-publicsuffixlist');
continue;
}
}
this.destroySelfie();
barrier = false;
};
return handler;
})();