mirror of https://github.com/gorhill/uBlock.git
Refactor selfie generation into a more flexible persistence mechanism
The motivation is to address the higher peak memory usage at launch time with 3rd-gen HNTrie when a selfie was present. The selfie generation prior to this change was to collect all filtering data into a single data structure, and then to serialize that whole structure at once into storage (using JSON.stringify). However, HNTrie serialization requires that a large UintArray32 be converted into a plain JS array, which itslef would be indirectly converted into a JSON string. This was the main reason why peak memory usage would be higher at launch from selfie, since the JSON string would need to be wholly unserialized into JS objects, which themselves would need to be converted into more specialized data structures (like that Uint32Array one). The solution to lower peak memory usage at launch is to refactor selfie generation to allow a more piecemeal approach: each filtering component is given the ability to serialize itself rather than to be forced to be embedded in the master selfie. With this approach, the HNTrie buffer can now serialize to its own storage by converting the buffer data directly into a string which can be directly sent to storage. This avoiding expensive intermediate steps such as converting into a JS array and then to a JSON string. As part of the refactoring, there was also opportunistic code upgrade to ES6 and Promise (eventually all of uBO's code will be proper ES6). Additionally, the polyfill to bring getBytesInUse() to Firefox has been revisited to replace the rather expensive previous implementation with an implementation with virtually no overhead.
This commit is contained in:
parent
83a3767a16
commit
ed7e34fb07
|
@ -7,6 +7,7 @@
|
||||||
"browser": false, // global variable in Firefox, Edge
|
"browser": false, // global variable in Firefox, Edge
|
||||||
"chrome": false, // global variable in Chromium, Chrome, Opera
|
"chrome": false, // global variable in Chromium, Chrome, Opera
|
||||||
"Components": false, // global variable in Firefox
|
"Components": false, // global variable in Firefox
|
||||||
|
"log": false,
|
||||||
"safari": false,
|
"safari": false,
|
||||||
"self": false,
|
"self": false,
|
||||||
"vAPI": false,
|
"vAPI": false,
|
||||||
|
|
|
@ -30,6 +30,10 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
vAPI.T0 = Date.now();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self);
|
vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<title>uBlock Origin</title>
|
<title>uBlock Origin</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<script src="js/console.js"></script>
|
||||||
<script src="lib/lz4/lz4-block-codec-any.js"></script>
|
<script src="lib/lz4/lz4-block-codec-any.js"></script>
|
||||||
<script src="lib/punycode.js"></script>
|
<script src="lib/punycode.js"></script>
|
||||||
<script src="lib/publicsuffixlist/publicsuffixlist.js"></script>
|
<script src="lib/publicsuffixlist/publicsuffixlist.js"></script>
|
||||||
|
|
145
src/js/assets.js
145
src/js/assets.js
|
@ -449,26 +449,22 @@ const assetCacheRegistryStartTime = Date.now();
|
||||||
let assetCacheRegistryPromise;
|
let assetCacheRegistryPromise;
|
||||||
let assetCacheRegistry = {};
|
let assetCacheRegistry = {};
|
||||||
|
|
||||||
const getAssetCacheRegistry = function(callback) {
|
const getAssetCacheRegistry = function() {
|
||||||
if ( assetCacheRegistryPromise === undefined ) {
|
if ( assetCacheRegistryPromise === undefined ) {
|
||||||
assetCacheRegistryPromise = new Promise(resolve => {
|
assetCacheRegistryPromise = new Promise(resolve => {
|
||||||
// start of executor
|
µBlock.cacheStorage.get('assetCacheRegistry', bin => {
|
||||||
µBlock.cacheStorage.get('assetCacheRegistry', bin => {
|
if (
|
||||||
if (
|
bin instanceof Object &&
|
||||||
bin instanceof Object &&
|
bin.assetCacheRegistry instanceof Object
|
||||||
bin.assetCacheRegistry instanceof Object
|
) {
|
||||||
) {
|
assetCacheRegistry = bin.assetCacheRegistry;
|
||||||
assetCacheRegistry = bin.assetCacheRegistry;
|
}
|
||||||
}
|
resolve();
|
||||||
resolve();
|
});
|
||||||
});
|
|
||||||
// end of executor
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
assetCacheRegistryPromise.then(( ) => {
|
return assetCacheRegistryPromise.then(( ) => assetCacheRegistry);
|
||||||
callback(assetCacheRegistry);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveAssetCacheRegistry = (function() {
|
const saveAssetCacheRegistry = (function() {
|
||||||
|
@ -513,11 +509,9 @@ const assetCacheRead = function(assetKey, callback) {
|
||||||
reportBack(bin[internalKey]);
|
reportBack(bin[internalKey]);
|
||||||
};
|
};
|
||||||
|
|
||||||
let onReady = function() {
|
getAssetCacheRegistry().then(( ) => {
|
||||||
µBlock.cacheStorage.get(internalKey, onAssetRead);
|
µBlock.cacheStorage.get(internalKey, onAssetRead);
|
||||||
};
|
});
|
||||||
|
|
||||||
getAssetCacheRegistry(onReady);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const assetCacheWrite = function(assetKey, details, callback) {
|
const assetCacheWrite = function(assetKey, details, callback) {
|
||||||
|
@ -542,7 +536,18 @@ const assetCacheWrite = function(assetKey, details, callback) {
|
||||||
if ( details instanceof Object && typeof details.url === 'string' ) {
|
if ( details instanceof Object && typeof details.url === 'string' ) {
|
||||||
entry.remoteURL = details.url;
|
entry.remoteURL = details.url;
|
||||||
}
|
}
|
||||||
µBlock.cacheStorage.set({ assetCacheRegistry, [internalKey]: content });
|
µBlock.cacheStorage.set(
|
||||||
|
{ [internalKey]: content },
|
||||||
|
details => {
|
||||||
|
if (
|
||||||
|
details instanceof Object &&
|
||||||
|
typeof details.bytesInUse === 'number'
|
||||||
|
) {
|
||||||
|
entry.byteLength = details.bytesInUse;
|
||||||
|
}
|
||||||
|
saveAssetCacheRegistry(true);
|
||||||
|
}
|
||||||
|
);
|
||||||
const result = { assetKey, content };
|
const result = { assetKey, content };
|
||||||
if ( typeof callback === 'function' ) {
|
if ( typeof callback === 'function' ) {
|
||||||
callback(result);
|
callback(result);
|
||||||
|
@ -550,14 +555,16 @@ const assetCacheWrite = function(assetKey, details, callback) {
|
||||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/248
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/248
|
||||||
fireNotification('after-asset-updated', result);
|
fireNotification('after-asset-updated', result);
|
||||||
};
|
};
|
||||||
getAssetCacheRegistry(onReady);
|
|
||||||
|
getAssetCacheRegistry().then(( ) => {
|
||||||
|
µBlock.cacheStorage.get(internalKey, onReady);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const assetCacheRemove = function(pattern, callback) {
|
const assetCacheRemove = function(pattern, callback) {
|
||||||
const onReady = function() {
|
getAssetCacheRegistry().then(cacheDict => {
|
||||||
const cacheDict = assetCacheRegistry,
|
const removedEntries = [];
|
||||||
removedEntries = [],
|
const removedContent = [];
|
||||||
removedContent = [];
|
|
||||||
for ( const assetKey in cacheDict ) {
|
for ( const assetKey in cacheDict ) {
|
||||||
if ( pattern instanceof RegExp && !pattern.test(assetKey) ) {
|
if ( pattern instanceof RegExp && !pattern.test(assetKey) ) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -582,14 +589,15 @@ const assetCacheRemove = function(pattern, callback) {
|
||||||
{ assetKey: removedEntries[i] }
|
{ assetKey: removedEntries[i] }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
getAssetCacheRegistry(onReady);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const assetCacheMarkAsDirty = function(pattern, exclude, callback) {
|
const assetCacheMarkAsDirty = function(pattern, exclude, callback) {
|
||||||
const onReady = function() {
|
if ( typeof exclude === 'function' ) {
|
||||||
const cacheDict = assetCacheRegistry;
|
callback = exclude;
|
||||||
|
exclude = undefined;
|
||||||
|
}
|
||||||
|
getAssetCacheRegistry().then(cacheDict => {
|
||||||
let mustSave = false;
|
let mustSave = false;
|
||||||
for ( const assetKey in cacheDict ) {
|
for ( const assetKey in cacheDict ) {
|
||||||
if ( pattern instanceof RegExp ) {
|
if ( pattern instanceof RegExp ) {
|
||||||
|
@ -617,12 +625,7 @@ const assetCacheMarkAsDirty = function(pattern, exclude, callback) {
|
||||||
if ( typeof callback === 'function' ) {
|
if ( typeof callback === 'function' ) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
if ( typeof exclude === 'function' ) {
|
|
||||||
callback = exclude;
|
|
||||||
exclude = undefined;
|
|
||||||
}
|
|
||||||
getAssetCacheRegistry(onReady);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -642,12 +645,12 @@ const stringIsNotEmpty = function(s) {
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
var readUserAsset = function(assetKey, callback) {
|
const readUserAsset = function(assetKey, callback) {
|
||||||
var reportBack = function(content) {
|
const reportBack = function(content) {
|
||||||
callback({ assetKey: assetKey, content: content });
|
callback({ assetKey: assetKey, content: content });
|
||||||
};
|
};
|
||||||
|
|
||||||
var onLoaded = function(bin) {
|
const onLoaded = function(bin) {
|
||||||
if ( !bin ) { return reportBack(''); }
|
if ( !bin ) { return reportBack(''); }
|
||||||
var content = '';
|
var content = '';
|
||||||
if ( typeof bin['cached_asset_content://assets/user/filters.txt'] === 'string' ) {
|
if ( typeof bin['cached_asset_content://assets/user/filters.txt'] === 'string' ) {
|
||||||
|
@ -671,7 +674,7 @@ var readUserAsset = function(assetKey, callback) {
|
||||||
}
|
}
|
||||||
return reportBack(content);
|
return reportBack(content);
|
||||||
};
|
};
|
||||||
var toRead = assetKey;
|
let toRead = assetKey;
|
||||||
if ( assetKey === µBlock.userFiltersPath ) {
|
if ( assetKey === µBlock.userFiltersPath ) {
|
||||||
toRead = [
|
toRead = [
|
||||||
assetKey,
|
assetKey,
|
||||||
|
@ -682,7 +685,7 @@ var readUserAsset = function(assetKey, callback) {
|
||||||
vAPI.storage.get(toRead, onLoaded);
|
vAPI.storage.get(toRead, onLoaded);
|
||||||
};
|
};
|
||||||
|
|
||||||
var saveUserAsset = function(assetKey, content, callback) {
|
const saveUserAsset = function(assetKey, content, callback) {
|
||||||
var bin = {};
|
var bin = {};
|
||||||
bin[assetKey] = content;
|
bin[assetKey] = content;
|
||||||
// TODO(seamless migration):
|
// TODO(seamless migration):
|
||||||
|
@ -711,27 +714,33 @@ api.get = function(assetKey, options, callback) {
|
||||||
callback = noopfunc;
|
callback = noopfunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
// start of executor
|
||||||
if ( assetKey === µBlock.userFiltersPath ) {
|
if ( assetKey === µBlock.userFiltersPath ) {
|
||||||
readUserAsset(assetKey, callback);
|
readUserAsset(assetKey, details => {
|
||||||
|
callback(details);
|
||||||
|
resolve(details);
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var assetDetails = {},
|
let assetDetails = {},
|
||||||
contentURLs,
|
contentURLs,
|
||||||
contentURL;
|
contentURL;
|
||||||
|
|
||||||
var reportBack = function(content, err) {
|
const reportBack = function(content, err) {
|
||||||
var details = { assetKey: assetKey, content: content };
|
const details = { assetKey: assetKey, content: content };
|
||||||
if ( err ) {
|
if ( err ) {
|
||||||
details.error = assetDetails.lastError = err;
|
details.error = assetDetails.lastError = err;
|
||||||
} else {
|
} else {
|
||||||
assetDetails.lastError = undefined;
|
assetDetails.lastError = undefined;
|
||||||
}
|
}
|
||||||
callback(details);
|
callback(details);
|
||||||
|
resolve(details);
|
||||||
};
|
};
|
||||||
|
|
||||||
var onContentNotLoaded = function() {
|
const onContentNotLoaded = function() {
|
||||||
var isExternal;
|
let isExternal;
|
||||||
while ( (contentURL = contentURLs.shift()) ) {
|
while ( (contentURL = contentURLs.shift()) ) {
|
||||||
isExternal = reIsExternalPath.test(contentURL);
|
isExternal = reIsExternalPath.test(contentURL);
|
||||||
if ( isExternal === false || assetDetails.hasLocalURL !== true ) {
|
if ( isExternal === false || assetDetails.hasLocalURL !== true ) {
|
||||||
|
@ -748,7 +757,7 @@ api.get = function(assetKey, options, callback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var onContentLoaded = function(details) {
|
const onContentLoaded = function(details) {
|
||||||
if ( stringIsNotEmpty(details.content) === false ) {
|
if ( stringIsNotEmpty(details.content) === false ) {
|
||||||
onContentNotLoaded();
|
onContentNotLoaded();
|
||||||
return;
|
return;
|
||||||
|
@ -762,7 +771,7 @@ api.get = function(assetKey, options, callback) {
|
||||||
reportBack(details.content);
|
reportBack(details.content);
|
||||||
};
|
};
|
||||||
|
|
||||||
var onCachedContentLoaded = function(details) {
|
const onCachedContentLoaded = function(details) {
|
||||||
if ( details.content !== '' ) {
|
if ( details.content !== '' ) {
|
||||||
return reportBack(details.content);
|
return reportBack(details.content);
|
||||||
}
|
}
|
||||||
|
@ -780,11 +789,13 @@ api.get = function(assetKey, options, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
assetCacheRead(assetKey, onCachedContentLoaded);
|
assetCacheRead(assetKey, onCachedContentLoaded);
|
||||||
|
// end of executor
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var getRemote = function(assetKey, callback) {
|
const getRemote = function(assetKey, callback) {
|
||||||
var assetDetails = {},
|
var assetDetails = {},
|
||||||
contentURLs,
|
contentURLs,
|
||||||
contentURL;
|
contentURL;
|
||||||
|
@ -852,10 +863,19 @@ var getRemote = function(assetKey, callback) {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
api.put = function(assetKey, content, callback) {
|
api.put = function(assetKey, content, callback) {
|
||||||
if ( reIsUserAsset.test(assetKey) ) {
|
return new Promise(resolve => {
|
||||||
return saveUserAsset(assetKey, content, callback);
|
const onDone = function(details) {
|
||||||
}
|
if ( typeof callback === 'function' ) {
|
||||||
assetCacheWrite(assetKey, content, callback);
|
callback(details);
|
||||||
|
}
|
||||||
|
resolve(details);
|
||||||
|
};
|
||||||
|
if ( reIsUserAsset.test(assetKey) ) {
|
||||||
|
saveUserAsset(assetKey, content, onDone);
|
||||||
|
} else {
|
||||||
|
assetCacheWrite(assetKey, content, onDone);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -895,7 +915,7 @@ api.metadata = function(callback) {
|
||||||
if ( cacheRegistryReady ) { onReady(); }
|
if ( cacheRegistryReady ) { onReady(); }
|
||||||
});
|
});
|
||||||
|
|
||||||
getAssetCacheRegistry(function() {
|
getAssetCacheRegistry().then(( ) => {
|
||||||
cacheRegistryReady = true;
|
cacheRegistryReady = true;
|
||||||
if ( assetRegistryReady ) { onReady(); }
|
if ( assetRegistryReady ) { onReady(); }
|
||||||
});
|
});
|
||||||
|
@ -903,6 +923,19 @@ api.metadata = function(callback) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
api.getBytesInUse = function() {
|
||||||
|
return getAssetCacheRegistry().then(cacheDict => {
|
||||||
|
let bytesUsed = 0;
|
||||||
|
for ( const assetKey in cacheDict ) {
|
||||||
|
if ( cacheDict.hasOwnProperty(assetKey) === false ) { continue; }
|
||||||
|
bytesUsed += cacheDict[assetKey].byteLength || 0;
|
||||||
|
}
|
||||||
|
return bytesUsed;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
api.purge = assetCacheMarkAsDirty;
|
api.purge = assetCacheMarkAsDirty;
|
||||||
|
|
||||||
api.remove = function(pattern, callback) {
|
api.remove = function(pattern, callback) {
|
||||||
|
@ -1013,7 +1046,7 @@ var updateNext = function() {
|
||||||
updateOne();
|
updateOne();
|
||||||
});
|
});
|
||||||
|
|
||||||
getAssetCacheRegistry(function(dict) {
|
getAssetCacheRegistry().then(dict => {
|
||||||
cacheDict = dict;
|
cacheDict = dict;
|
||||||
if ( !assetDict ) { return; }
|
if ( !assetDict ) { return; }
|
||||||
updateOne();
|
updateOne();
|
||||||
|
|
|
@ -46,6 +46,7 @@ const µBlock = (function() { // jshint ignore:line
|
||||||
cacheStorageAPI: 'unset',
|
cacheStorageAPI: 'unset',
|
||||||
cacheStorageCompression: true,
|
cacheStorageCompression: true,
|
||||||
cacheControlForFirefox1376932: 'no-cache, no-store, must-revalidate',
|
cacheControlForFirefox1376932: 'no-cache, no-store, must-revalidate',
|
||||||
|
consoleLogLevel: 'unset',
|
||||||
debugScriptlets: false,
|
debugScriptlets: false,
|
||||||
disableWebAssembly: false,
|
disableWebAssembly: false,
|
||||||
ignoreRedirectFilters: false,
|
ignoreRedirectFilters: false,
|
||||||
|
@ -53,6 +54,7 @@ const µBlock = (function() { // jshint ignore:line
|
||||||
manualUpdateAssetFetchPeriod: 500,
|
manualUpdateAssetFetchPeriod: 500,
|
||||||
popupFontSize: 'unset',
|
popupFontSize: 'unset',
|
||||||
requestJournalProcessPeriod: 1000,
|
requestJournalProcessPeriod: 1000,
|
||||||
|
selfieAfter: 11,
|
||||||
strictBlockingBypassDuration: 120,
|
strictBlockingBypassDuration: 120,
|
||||||
suspendTabsUntilReady: false,
|
suspendTabsUntilReady: false,
|
||||||
userResourcesLocation: 'unset'
|
userResourcesLocation: 'unset'
|
||||||
|
@ -95,13 +97,13 @@ const µBlock = (function() { // jshint ignore:line
|
||||||
|
|
||||||
hiddenSettingsDefault: hiddenSettingsDefault,
|
hiddenSettingsDefault: hiddenSettingsDefault,
|
||||||
hiddenSettings: (function() {
|
hiddenSettings: (function() {
|
||||||
let out = Object.assign({}, hiddenSettingsDefault),
|
const out = Object.assign({}, hiddenSettingsDefault),
|
||||||
json = vAPI.localStorage.getItem('immediateHiddenSettings');
|
json = vAPI.localStorage.getItem('immediateHiddenSettings');
|
||||||
if ( typeof json === 'string' ) {
|
if ( typeof json === 'string' ) {
|
||||||
try {
|
try {
|
||||||
let o = JSON.parse(json);
|
const o = JSON.parse(json);
|
||||||
if ( o instanceof Object ) {
|
if ( o instanceof Object ) {
|
||||||
for ( let k in o ) {
|
for ( const k in o ) {
|
||||||
if ( out.hasOwnProperty(k) ) {
|
if ( out.hasOwnProperty(k) ) {
|
||||||
out[k] = o[k];
|
out[k] = o[k];
|
||||||
}
|
}
|
||||||
|
@ -111,8 +113,6 @@ const µBlock = (function() { // jshint ignore:line
|
||||||
catch(ex) {
|
catch(ex) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove once 1.15.12+ is widespread.
|
|
||||||
vAPI.localStorage.removeItem('hiddenSettings');
|
|
||||||
return out;
|
return out;
|
||||||
})(),
|
})(),
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ const µBlock = (function() { // jshint ignore:line
|
||||||
// Read-only
|
// Read-only
|
||||||
systemSettings: {
|
systemSettings: {
|
||||||
compiledMagic: 6, // Increase when compiled format changes
|
compiledMagic: 6, // Increase when compiled format changes
|
||||||
selfieMagic: 7 // Increase when selfie format changes
|
selfieMagic: 8 // Increase when selfie format changes
|
||||||
},
|
},
|
||||||
|
|
||||||
restoreBackupSettings: {
|
restoreBackupSettings: {
|
||||||
|
@ -161,8 +161,6 @@ const µBlock = (function() { // jshint ignore:line
|
||||||
selectedFilterLists: [],
|
selectedFilterLists: [],
|
||||||
availableFilterLists: {},
|
availableFilterLists: {},
|
||||||
|
|
||||||
selfieAfter: 17 * oneMinute,
|
|
||||||
|
|
||||||
pageStores: new Map(),
|
pageStores: new Map(),
|
||||||
pageStoresToken: 0,
|
pageStoresToken: 0,
|
||||||
|
|
||||||
|
|
|
@ -326,17 +326,27 @@
|
||||||
if ( typeof callback !== 'function' ) {
|
if ( typeof callback !== 'function' ) {
|
||||||
callback = noopfn;
|
callback = noopfn;
|
||||||
}
|
}
|
||||||
let keys = Object.keys(keyvalStore);
|
const keys = Object.keys(keyvalStore);
|
||||||
if ( keys.length === 0 ) { return callback(); }
|
if ( keys.length === 0 ) { return callback(); }
|
||||||
let promises = [ getDb() ];
|
const promises = [ getDb() ];
|
||||||
let entries = [];
|
const entries = [];
|
||||||
let dontCompress = µBlock.hiddenSettings.cacheStorageCompression !== true;
|
const dontCompress = µBlock.hiddenSettings.cacheStorageCompression !== true;
|
||||||
let handleEncodingResult = result => {
|
let bytesInUse = 0;
|
||||||
|
const handleEncodingResult = result => {
|
||||||
|
if ( typeof result.data === 'string' ) {
|
||||||
|
bytesInUse += result.data.length;
|
||||||
|
} else if ( result.data instanceof Blob ) {
|
||||||
|
bytesInUse += result.data.size;
|
||||||
|
}
|
||||||
entries.push({ key: result.key, value: result.data });
|
entries.push({ key: result.key, value: result.data });
|
||||||
};
|
};
|
||||||
for ( let key of keys ) {
|
for ( const key of keys ) {
|
||||||
let data = keyvalStore[key];
|
const data = keyvalStore[key];
|
||||||
if ( typeof data !== 'string' || dontCompress ) {
|
const isString = typeof data === 'string';
|
||||||
|
if ( isString === false || dontCompress ) {
|
||||||
|
if ( isString ) {
|
||||||
|
bytesInUse += data.length;
|
||||||
|
}
|
||||||
entries.push({ key, value: data });
|
entries.push({ key, value: data });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -346,20 +356,20 @@
|
||||||
}
|
}
|
||||||
Promise.all(promises).then(( ) => {
|
Promise.all(promises).then(( ) => {
|
||||||
if ( !db ) { return callback(); }
|
if ( !db ) { return callback(); }
|
||||||
let finish = ( ) => {
|
const finish = ( ) => {
|
||||||
dbBytesInUse = undefined;
|
dbBytesInUse = undefined;
|
||||||
if ( callback === undefined ) { return; }
|
if ( callback === undefined ) { return; }
|
||||||
let cb = callback;
|
let cb = callback;
|
||||||
callback = undefined;
|
callback = undefined;
|
||||||
cb();
|
cb({ bytesInUse });
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
let transaction = db.transaction(STORAGE_NAME, 'readwrite');
|
const transaction = db.transaction(STORAGE_NAME, 'readwrite');
|
||||||
transaction.oncomplete =
|
transaction.oncomplete =
|
||||||
transaction.onerror =
|
transaction.onerror =
|
||||||
transaction.onabort = finish;
|
transaction.onabort = finish;
|
||||||
let table = transaction.objectStore(STORAGE_NAME);
|
const table = transaction.objectStore(STORAGE_NAME);
|
||||||
for ( let entry of entries ) {
|
for ( const entry of entries ) {
|
||||||
table.put(entry);
|
table.put(entry);
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2019-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
self.log = (function() {
|
||||||
|
const noopFunc = function() {};
|
||||||
|
const info = function(s) { console.log(`[uBO] ${s}`); };
|
||||||
|
return {
|
||||||
|
get verbosity( ) { return; },
|
||||||
|
set verbosity(level) {
|
||||||
|
this.info = console.info = level === 'info' ? info : noopFunc;
|
||||||
|
},
|
||||||
|
info,
|
||||||
|
};
|
||||||
|
})();
|
|
@ -355,7 +355,13 @@ HNTrieContainer.prototype = {
|
||||||
return trieRef;
|
return trieRef;
|
||||||
},
|
},
|
||||||
|
|
||||||
serialize: function() {
|
serialize: function(encoder) {
|
||||||
|
if ( encoder instanceof Object ) {
|
||||||
|
return encoder.encode(
|
||||||
|
this.buf32.buffer,
|
||||||
|
this.buf32[HNTRIE_CHAR1_SLOT]
|
||||||
|
);
|
||||||
|
}
|
||||||
return Array.from(
|
return Array.from(
|
||||||
new Uint32Array(
|
new Uint32Array(
|
||||||
this.buf32.buffer,
|
this.buf32.buffer,
|
||||||
|
@ -365,23 +371,29 @@ HNTrieContainer.prototype = {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
unserialize: function(selfie) {
|
unserialize: function(selfie, decoder) {
|
||||||
const len = (selfie.length << 2) + HNTRIE_PAGE_SIZE-1 & ~(HNTRIE_PAGE_SIZE-1);
|
const shouldDecode = typeof selfie === 'string';
|
||||||
|
let byteLength = shouldDecode
|
||||||
|
? decoder.decodeSize(selfie)
|
||||||
|
: selfie.length << 2;
|
||||||
|
byteLength = byteLength + HNTRIE_PAGE_SIZE-1 & ~(HNTRIE_PAGE_SIZE-1);
|
||||||
if ( this.wasmMemory !== null ) {
|
if ( this.wasmMemory !== null ) {
|
||||||
const pageCountBefore = this.buf.length >>> 16;
|
const pageCountBefore = this.buf.length >>> 16;
|
||||||
const pageCountAfter = len >>> 16;
|
const pageCountAfter = byteLength >>> 16;
|
||||||
if ( pageCountAfter > pageCountBefore ) {
|
if ( pageCountAfter > pageCountBefore ) {
|
||||||
this.wasmMemory.grow(pageCountAfter - pageCountBefore);
|
this.wasmMemory.grow(pageCountAfter - pageCountBefore);
|
||||||
this.buf = new Uint8Array(this.wasmMemory.buffer);
|
this.buf = new Uint8Array(this.wasmMemory.buffer);
|
||||||
this.buf32 = new Uint32Array(this.buf.buffer);
|
this.buf32 = new Uint32Array(this.buf.buffer);
|
||||||
}
|
}
|
||||||
} else {
|
} else if ( byteLength > this.buf.length ) {
|
||||||
if ( len > this.buf.length ) {
|
this.buf = new Uint8Array(byteLength);
|
||||||
this.buf = new Uint8Array(len);
|
this.buf32 = new Uint32Array(this.buf.buffer);
|
||||||
this.buf32 = new Uint32Array(this.buf.buffer);
|
}
|
||||||
}
|
if ( shouldDecode ) {
|
||||||
|
decoder.decode(selfie, this.buf.buffer);
|
||||||
|
} else {
|
||||||
|
this.buf32.set(selfie);
|
||||||
}
|
}
|
||||||
this.buf32.set(selfie);
|
|
||||||
this.needle = '';
|
this.needle = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -684,6 +696,6 @@ HNTrieContainer.prototype.HNTrieRef.prototype = {
|
||||||
WebAssembly.compileStreaming
|
WebAssembly.compileStreaming
|
||||||
).catch(reason => {
|
).catch(reason => {
|
||||||
HNTrieContainer.wasmModulePromise = null;
|
HNTrieContainer.wasmModulePromise = null;
|
||||||
console.info(reason);
|
log.info(reason);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -29,12 +29,12 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const warResolve = (function() {
|
const warResolve = (function() {
|
||||||
var warPairs = [];
|
let warPairs = [];
|
||||||
|
|
||||||
var onPairsReady = function() {
|
const onPairsReady = function() {
|
||||||
var reng = µBlock.redirectEngine;
|
const reng = µBlock.redirectEngine;
|
||||||
for ( var i = 0; i < warPairs.length; i += 2 ) {
|
for ( let i = 0; i < warPairs.length; i += 2 ) {
|
||||||
var resource = reng.resources.get(warPairs[i+0]);
|
const resource = reng.resources.get(warPairs[i+0]);
|
||||||
if ( resource === undefined ) { continue; }
|
if ( resource === undefined ) { continue; }
|
||||||
resource.warURL = vAPI.getURL(
|
resource.warURL = vAPI.getURL(
|
||||||
'/web_accessible_resources/' + warPairs[i+1]
|
'/web_accessible_resources/' + warPairs[i+1]
|
||||||
|
@ -48,15 +48,15 @@ const warResolve = (function() {
|
||||||
return onPairsReady();
|
return onPairsReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
var onPairsLoaded = function(details) {
|
const onPairsLoaded = function(details) {
|
||||||
var marker = '>>>>>';
|
const marker = '>>>>>';
|
||||||
var pos = details.content.indexOf(marker);
|
const pos = details.content.indexOf(marker);
|
||||||
if ( pos === -1 ) { return; }
|
if ( pos === -1 ) { return; }
|
||||||
var pairs = details.content.slice(pos + marker.length)
|
const pairs = details.content.slice(pos + marker.length)
|
||||||
.trim()
|
.trim()
|
||||||
.split('\n');
|
.split('\n');
|
||||||
if ( (pairs.length & 1) !== 0 ) { return; }
|
if ( (pairs.length & 1) !== 0 ) { return; }
|
||||||
for ( var i = 0; i < pairs.length; i++ ) {
|
for ( let i = 0; i < pairs.length; i++ ) {
|
||||||
pairs[i] = pairs[i].trim();
|
pairs[i] = pairs[i].trim();
|
||||||
}
|
}
|
||||||
warPairs = pairs;
|
warPairs = pairs;
|
||||||
|
@ -64,7 +64,7 @@ const warResolve = (function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
µBlock.assets.fetchText(
|
µBlock.assets.fetchText(
|
||||||
'/web_accessible_resources/imported.txt?secret=' + vAPI.warSecret,
|
`/web_accessible_resources/imported.txt?secret=${vAPI.warSecret}`,
|
||||||
onPairsLoaded
|
onPairsLoaded
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -374,18 +374,17 @@ RedirectEngine.prototype.supportedTypes = new Map([
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
RedirectEngine.prototype.toSelfie = function() {
|
RedirectEngine.prototype.toSelfie = function(path) {
|
||||||
// Because rules may contains RegExp instances, we need to manually
|
// Because rules may contains RegExp instances, we need to manually
|
||||||
// convert it to a serializable format. The serialized format must be
|
// convert it to a serializable format. The serialized format must be
|
||||||
// suitable to be used as an argument to the Map() constructor.
|
// suitable to be used as an argument to the Map() constructor.
|
||||||
var rules = [],
|
const rules = [];
|
||||||
rule, entries, i, entry;
|
for ( const item of this.rules ) {
|
||||||
for ( var item of this.rules ) {
|
const rule = [ item[0], [] ];
|
||||||
rule = [ item[0], [] ];
|
const entries = item[1];
|
||||||
entries = item[1];
|
let i = entries.length;
|
||||||
i = entries.length;
|
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
entry = entries[i];
|
const entry = entries[i];
|
||||||
rule[1].push({
|
rule[1].push({
|
||||||
tok: entry.tok,
|
tok: entry.tok,
|
||||||
pat: entry.pat instanceof RegExp ? entry.pat.source : entry.pat
|
pat: entry.pat instanceof RegExp ? entry.pat.source : entry.pat
|
||||||
|
@ -393,23 +392,34 @@ RedirectEngine.prototype.toSelfie = function() {
|
||||||
}
|
}
|
||||||
rules.push(rule);
|
rules.push(rule);
|
||||||
}
|
}
|
||||||
return {
|
return µBlock.assets.put(
|
||||||
rules: rules,
|
`${path}/main`,
|
||||||
ruleTypes: Array.from(this.ruleTypes),
|
JSON.stringify({
|
||||||
ruleSources: Array.from(this.ruleSources),
|
rules: rules,
|
||||||
ruleDestinations: Array.from(this.ruleDestinations)
|
ruleTypes: Array.from(this.ruleTypes),
|
||||||
};
|
ruleSources: Array.from(this.ruleSources),
|
||||||
|
ruleDestinations: Array.from(this.ruleDestinations)
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
RedirectEngine.prototype.fromSelfie = function(selfie) {
|
RedirectEngine.prototype.fromSelfie = function(path) {
|
||||||
this.rules = new Map(selfie.rules);
|
return µBlock.assets.get(`${path}/main`).then(details => {
|
||||||
this.ruleTypes = new Set(selfie.ruleTypes);
|
let selfie;
|
||||||
this.ruleSources = new Set(selfie.ruleSources);
|
try {
|
||||||
this.ruleDestinations = new Set(selfie.ruleDestinations);
|
selfie = JSON.parse(details.content);
|
||||||
this.modifyTime = Date.now();
|
} catch (ex) {
|
||||||
return true;
|
}
|
||||||
|
if ( selfie instanceof Object === false ) { return false; }
|
||||||
|
this.rules = new Map(selfie.rules);
|
||||||
|
this.ruleTypes = new Set(selfie.ruleTypes);
|
||||||
|
this.ruleSources = new Set(selfie.ruleSources);
|
||||||
|
this.ruleDestinations = new Set(selfie.ruleDestinations);
|
||||||
|
this.modifyTime = Date.now();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -494,41 +504,46 @@ RedirectEngine.prototype.resourcesFromString = function(text) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
let resourcesSelfieVersion = 3;
|
const resourcesSelfieVersion = 3;
|
||||||
|
|
||||||
RedirectEngine.prototype.selfieFromResources = function() {
|
RedirectEngine.prototype.selfieFromResources = function() {
|
||||||
let selfie = {
|
µBlock.assets.put(
|
||||||
version: resourcesSelfieVersion,
|
'compiled/redirectEngine/resources',
|
||||||
resources: Array.from(this.resources)
|
JSON.stringify({
|
||||||
};
|
version: resourcesSelfieVersion,
|
||||||
µBlock.cacheStorage.set({ resourcesSelfie: JSON.stringify(selfie) });
|
resources: Array.from(this.resources)
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
RedirectEngine.prototype.resourcesFromSelfie = function(callback) {
|
RedirectEngine.prototype.resourcesFromSelfie = function() {
|
||||||
µBlock.cacheStorage.get('resourcesSelfie', bin => {
|
return µBlock.assets.get(
|
||||||
let selfie = bin && bin.resourcesSelfie;
|
'compiled/redirectEngine/resources'
|
||||||
if ( typeof selfie === 'string' ) {
|
).then(details => {
|
||||||
try {
|
let selfie;
|
||||||
selfie = JSON.parse(selfie);
|
try {
|
||||||
} catch(ex) {
|
selfie = JSON.parse(details.content);
|
||||||
}
|
} catch(ex) {
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
selfie instanceof Object === false ||
|
selfie instanceof Object === false ||
|
||||||
selfie.version !== resourcesSelfieVersion ||
|
selfie.version !== resourcesSelfieVersion ||
|
||||||
Array.isArray(selfie.resources) === false
|
Array.isArray(selfie.resources) === false
|
||||||
) {
|
) {
|
||||||
return callback(false);
|
return false;
|
||||||
}
|
}
|
||||||
this.resources = new Map();
|
this.resources = new Map();
|
||||||
for ( let entry of selfie.resources ) {
|
for ( const [ token, entry ] of selfie.resources ) {
|
||||||
this.resources.set(entry[0], RedirectEntry.fromSelfie(entry[1]));
|
this.resources.set(token, RedirectEntry.fromSelfie(entry));
|
||||||
}
|
}
|
||||||
callback(true);
|
return true;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
RedirectEngine.prototype.invalidateResourcesSelfie = function() {
|
RedirectEngine.prototype.invalidateResourcesSelfie = function() {
|
||||||
|
µBlock.assets.remove('compiled/redirectEngine/resources');
|
||||||
|
|
||||||
|
// TODO: obsolete, remove eventually
|
||||||
µBlock.cacheStorage.remove('resourcesSelfie');
|
µBlock.cacheStorage.remove('resourcesSelfie');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,8 @@ var onAllReady = function() {
|
||||||
|
|
||||||
µb.contextMenu.update(null);
|
µb.contextMenu.update(null);
|
||||||
µb.firstInstall = false;
|
µb.firstInstall = false;
|
||||||
|
|
||||||
|
log.info(`All ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -137,22 +139,29 @@ let initializeTabs = function() {
|
||||||
// Filtering engines dependencies:
|
// Filtering engines dependencies:
|
||||||
// - PSL
|
// - PSL
|
||||||
|
|
||||||
var onPSLReady = function() {
|
const onPSLReady = function() {
|
||||||
µb.selfieManager.load(function(valid) {
|
log.info(`PSL ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||||
|
|
||||||
|
µb.selfieManager.load().then(valid => {
|
||||||
if ( valid === true ) {
|
if ( valid === true ) {
|
||||||
return onAllReady();
|
log.info(`Selfie ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||||
|
onAllReady();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
µb.loadFilterLists(onAllReady);
|
µb.loadFilterLists(( ) => {
|
||||||
|
log.info(`Filter lists ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||||
|
onAllReady();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var onCommandShortcutsReady = function(commandShortcuts) {
|
const onCommandShortcutsReady = function(commandShortcuts) {
|
||||||
if ( Array.isArray(commandShortcuts) === false ) { return; }
|
if ( Array.isArray(commandShortcuts) === false ) { return; }
|
||||||
µb.commandShortcuts = new Map(commandShortcuts);
|
µb.commandShortcuts = new Map(commandShortcuts);
|
||||||
if ( µb.canUpdateShortcuts === false ) { return; }
|
if ( µb.canUpdateShortcuts === false ) { return; }
|
||||||
for ( let entry of commandShortcuts ) {
|
for ( const entry of commandShortcuts ) {
|
||||||
vAPI.commands.update({ name: entry[0], shortcut: entry[1] });
|
vAPI.commands.update({ name: entry[0], shortcut: entry[1] });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -161,7 +170,7 @@ var onCommandShortcutsReady = function(commandShortcuts) {
|
||||||
|
|
||||||
// To bring older versions up to date
|
// To bring older versions up to date
|
||||||
|
|
||||||
var onVersionReady = function(lastVersion) {
|
const onVersionReady = function(lastVersion) {
|
||||||
if ( lastVersion === vAPI.app.version ) { return; }
|
if ( lastVersion === vAPI.app.version ) { return; }
|
||||||
|
|
||||||
// Since AMO does not allow updating resources.txt, force a reload when a
|
// Since AMO does not allow updating resources.txt, force a reload when a
|
||||||
|
@ -176,7 +185,7 @@ var onVersionReady = function(lastVersion) {
|
||||||
|
|
||||||
// If unused, just comment out for when we need to compare versions in the
|
// If unused, just comment out for when we need to compare versions in the
|
||||||
// future.
|
// future.
|
||||||
let intFromVersion = function(s) {
|
const intFromVersion = function(s) {
|
||||||
let parts = s.match(/(?:^|\.|b|rc)\d+/g);
|
let parts = s.match(/(?:^|\.|b|rc)\d+/g);
|
||||||
if ( parts === null ) { return 0; }
|
if ( parts === null ) { return 0; }
|
||||||
let vint = 0;
|
let vint = 0;
|
||||||
|
@ -223,7 +232,7 @@ var onVersionReady = function(lastVersion) {
|
||||||
// Whitelist parser needs PSL to be ready.
|
// Whitelist parser needs PSL to be ready.
|
||||||
// gorhill 2014-12-15: not anymore
|
// gorhill 2014-12-15: not anymore
|
||||||
|
|
||||||
var onNetWhitelistReady = function(netWhitelistRaw) {
|
const onNetWhitelistReady = function(netWhitelistRaw) {
|
||||||
µb.netWhitelist = µb.whitelistFromString(netWhitelistRaw);
|
µb.netWhitelist = µb.whitelistFromString(netWhitelistRaw);
|
||||||
µb.netWhitelistModifyTime = Date.now();
|
µb.netWhitelistModifyTime = Date.now();
|
||||||
};
|
};
|
||||||
|
@ -232,8 +241,10 @@ var onNetWhitelistReady = function(netWhitelistRaw) {
|
||||||
|
|
||||||
// User settings are in memory
|
// User settings are in memory
|
||||||
|
|
||||||
var onUserSettingsReady = function(fetched) {
|
const onUserSettingsReady = function(fetched) {
|
||||||
var userSettings = µb.userSettings;
|
log.info(`User settings ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||||
|
|
||||||
|
const userSettings = µb.userSettings;
|
||||||
|
|
||||||
fromFetch(userSettings, fetched);
|
fromFetch(userSettings, fetched);
|
||||||
|
|
||||||
|
@ -264,7 +275,7 @@ var onUserSettingsReady = function(fetched) {
|
||||||
|
|
||||||
// Housekeeping, as per system setting changes
|
// Housekeeping, as per system setting changes
|
||||||
|
|
||||||
var onSystemSettingsReady = function(fetched) {
|
const onSystemSettingsReady = function(fetched) {
|
||||||
var mustSaveSystemSettings = false;
|
var mustSaveSystemSettings = false;
|
||||||
if ( fetched.compiledMagic !== µb.systemSettings.compiledMagic ) {
|
if ( fetched.compiledMagic !== µb.systemSettings.compiledMagic ) {
|
||||||
µb.assets.remove(/^compiled\//);
|
µb.assets.remove(/^compiled\//);
|
||||||
|
@ -282,7 +293,9 @@ var onSystemSettingsReady = function(fetched) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var onFirstFetchReady = function(fetched) {
|
const onFirstFetchReady = function(fetched) {
|
||||||
|
log.info(`First fetch ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/747
|
// https://github.com/gorhill/uBlock/issues/747
|
||||||
µb.firstInstall = fetched.version === '0.0.0.0';
|
µb.firstInstall = fetched.version === '0.0.0.0';
|
||||||
|
|
||||||
|
@ -295,10 +308,7 @@ var onFirstFetchReady = function(fetched) {
|
||||||
onVersionReady(fetched.version);
|
onVersionReady(fetched.version);
|
||||||
onCommandShortcutsReady(fetched.commandShortcuts);
|
onCommandShortcutsReady(fetched.commandShortcuts);
|
||||||
|
|
||||||
Promise.all([
|
µb.loadPublicSuffixList().then(( ) => {
|
||||||
µb.loadPublicSuffixList(),
|
|
||||||
µb.staticNetFilteringEngine.readyToUse()
|
|
||||||
]).then(( ) => {
|
|
||||||
onPSLReady();
|
onPSLReady();
|
||||||
});
|
});
|
||||||
µb.loadRedirectResources();
|
µb.loadRedirectResources();
|
||||||
|
@ -306,31 +316,27 @@ var onFirstFetchReady = function(fetched) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var toFetch = function(from, fetched) {
|
const toFetch = function(from, fetched) {
|
||||||
for ( var k in from ) {
|
for ( const k in from ) {
|
||||||
if ( from.hasOwnProperty(k) === false ) {
|
if ( from.hasOwnProperty(k) === false ) { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
fetched[k] = from[k];
|
fetched[k] = from[k];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var fromFetch = function(to, fetched) {
|
const fromFetch = function(to, fetched) {
|
||||||
for ( var k in to ) {
|
for ( const k in to ) {
|
||||||
if ( to.hasOwnProperty(k) === false ) {
|
if ( to.hasOwnProperty(k) === false ) { continue; }
|
||||||
continue;
|
if ( fetched.hasOwnProperty(k) === false ) { continue; }
|
||||||
}
|
|
||||||
if ( fetched.hasOwnProperty(k) === false ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
to[k] = fetched[k];
|
to[k] = fetched[k];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var onSelectedFilterListsLoaded = function() {
|
const onSelectedFilterListsLoaded = function() {
|
||||||
var fetchableProps = {
|
log.info(`List selection ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||||
|
|
||||||
|
const fetchableProps = {
|
||||||
'commandShortcuts': [],
|
'commandShortcuts': [],
|
||||||
'compiledMagic': 0,
|
'compiledMagic': 0,
|
||||||
'dynamicFilteringString': [
|
'dynamicFilteringString': [
|
||||||
|
@ -371,7 +377,8 @@ var onSelectedFilterListsLoaded = function() {
|
||||||
// compatibility, this means a special asynchronous call to load selected
|
// compatibility, this means a special asynchronous call to load selected
|
||||||
// filter lists.
|
// filter lists.
|
||||||
|
|
||||||
var onAdminSettingsRestored = function() {
|
const onAdminSettingsRestored = function() {
|
||||||
|
log.info(`Admin settings ready ${Date.now()-vAPI.T0} ms after launch`);
|
||||||
µb.loadSelectedFilterLists(onSelectedFilterListsLoaded);
|
µb.loadSelectedFilterLists(onSelectedFilterListsLoaded);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -821,18 +821,30 @@
|
||||||
µb.htmlFilteringEngine.fromCompiledContent(reader, options);
|
µb.htmlFilteringEngine.fromCompiledContent(reader, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
api.toSelfie = function() {
|
api.toSelfie = function(path) {
|
||||||
return {
|
return µBlock.assets.put(
|
||||||
cosmetic: µb.cosmeticFilteringEngine.toSelfie(),
|
`${path}/main`,
|
||||||
scriptlets: µb.scriptletFilteringEngine.toSelfie(),
|
JSON.stringify({
|
||||||
html: µb.htmlFilteringEngine.toSelfie()
|
cosmetic: µb.cosmeticFilteringEngine.toSelfie(),
|
||||||
};
|
scriptlets: µb.scriptletFilteringEngine.toSelfie(),
|
||||||
|
html: µb.htmlFilteringEngine.toSelfie()
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
api.fromSelfie = function(selfie) {
|
api.fromSelfie = function(path) {
|
||||||
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmetic);
|
return µBlock.assets.get(`${path}/main`).then(details => {
|
||||||
µb.scriptletFilteringEngine.fromSelfie(selfie.scriptlets);
|
let selfie;
|
||||||
µb.htmlFilteringEngine.fromSelfie(selfie.html);
|
try {
|
||||||
|
selfie = JSON.parse(details.content);
|
||||||
|
} catch (ex) {
|
||||||
|
}
|
||||||
|
if ( selfie instanceof Object === false ) { return false; }
|
||||||
|
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmetic);
|
||||||
|
µb.scriptletFilteringEngine.fromSelfie(selfie.scriptlets);
|
||||||
|
µb.htmlFilteringEngine.fromSelfie(selfie.html);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return api;
|
return api;
|
||||||
|
|
|
@ -2105,21 +2105,21 @@ FilterContainer.prototype.readyToUse = function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
FilterContainer.prototype.toSelfie = function() {
|
FilterContainer.prototype.toSelfie = function(path) {
|
||||||
let categoriesToSelfie = function(categoryMap) {
|
const categoriesToSelfie = function(categoryMap) {
|
||||||
let selfie = [];
|
const selfie = [];
|
||||||
for ( let categoryEntry of categoryMap ) {
|
for ( const [ catbits, bucket ] of categoryMap ) {
|
||||||
let tokenEntries = [];
|
const tokenEntries = [];
|
||||||
for ( let tokenEntry of categoryEntry[1] ) {
|
for ( const [ token, filter ] of bucket ) {
|
||||||
tokenEntries.push([ tokenEntry[0], tokenEntry[1].compile() ]);
|
tokenEntries.push([ token, filter.compile() ]);
|
||||||
}
|
}
|
||||||
selfie.push([ categoryEntry[0], tokenEntries ]);
|
selfie.push([ catbits, tokenEntries ]);
|
||||||
}
|
}
|
||||||
return selfie;
|
return selfie;
|
||||||
};
|
};
|
||||||
|
|
||||||
let dataFiltersToSelfie = function(dataFilters) {
|
const dataFiltersToSelfie = function(dataFilters) {
|
||||||
let selfie = [];
|
const selfie = [];
|
||||||
for ( let entry of dataFilters.values() ) {
|
for ( let entry of dataFilters.values() ) {
|
||||||
do {
|
do {
|
||||||
selfie.push(entry.compile());
|
selfie.push(entry.compile());
|
||||||
|
@ -2129,47 +2129,72 @@ FilterContainer.prototype.toSelfie = function() {
|
||||||
return selfie;
|
return selfie;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return Promise.all([
|
||||||
processedFilterCount: this.processedFilterCount,
|
µBlock.assets.put(
|
||||||
acceptedCount: this.acceptedCount,
|
`${path}/trieContainer`,
|
||||||
rejectedCount: this.rejectedCount,
|
FilterHostnameDict.trieContainer.serialize(µBlock.base128)
|
||||||
allowFilterCount: this.allowFilterCount,
|
),
|
||||||
blockFilterCount: this.blockFilterCount,
|
µBlock.assets.put(
|
||||||
discardedCount: this.discardedCount,
|
`${path}/main`,
|
||||||
trieContainer: FilterHostnameDict.trieContainer.serialize(),
|
JSON.stringify({
|
||||||
categories: categoriesToSelfie(this.categories),
|
processedFilterCount: this.processedFilterCount,
|
||||||
dataFilters: dataFiltersToSelfie(this.dataFilters)
|
acceptedCount: this.acceptedCount,
|
||||||
};
|
rejectedCount: this.rejectedCount,
|
||||||
|
allowFilterCount: this.allowFilterCount,
|
||||||
|
blockFilterCount: this.blockFilterCount,
|
||||||
|
discardedCount: this.discardedCount,
|
||||||
|
categories: categoriesToSelfie(this.categories),
|
||||||
|
dataFilters: dataFiltersToSelfie(this.dataFilters),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
FilterContainer.prototype.fromSelfie = function(selfie) {
|
FilterContainer.prototype.fromSelfie = function(path) {
|
||||||
this.frozen = true;
|
return Promise.all([
|
||||||
this.processedFilterCount = selfie.processedFilterCount;
|
µBlock.assets.get(`${path}/trieContainer`).then(details => {
|
||||||
this.acceptedCount = selfie.acceptedCount;
|
FilterHostnameDict.trieContainer.unserialize(
|
||||||
this.rejectedCount = selfie.rejectedCount;
|
details.content,
|
||||||
this.allowFilterCount = selfie.allowFilterCount;
|
µBlock.base128
|
||||||
this.blockFilterCount = selfie.blockFilterCount;
|
);
|
||||||
this.discardedCount = selfie.discardedCount;
|
return true;
|
||||||
FilterHostnameDict.trieContainer.unserialize(selfie.trieContainer);
|
}),
|
||||||
|
µBlock.assets.get(`${path}/main`).then(details => {
|
||||||
for ( let categoryEntry of selfie.categories ) {
|
let selfie;
|
||||||
let tokenMap = new Map();
|
try {
|
||||||
for ( let tokenEntry of categoryEntry[1] ) {
|
selfie = JSON.parse(details.content);
|
||||||
tokenMap.set(tokenEntry[0], filterFromCompiledData(tokenEntry[1]));
|
} catch (ex) {
|
||||||
}
|
}
|
||||||
this.categories.set(categoryEntry[0], tokenMap);
|
if ( selfie instanceof Object === false ) { return false; }
|
||||||
}
|
this.frozen = true;
|
||||||
|
this.processedFilterCount = selfie.processedFilterCount;
|
||||||
for ( let dataEntry of selfie.dataFilters ) {
|
this.acceptedCount = selfie.acceptedCount;
|
||||||
let entry = FilterDataHolderEntry.load(dataEntry);
|
this.rejectedCount = selfie.rejectedCount;
|
||||||
let bucket = this.dataFilters.get(entry.tokenHash);
|
this.allowFilterCount = selfie.allowFilterCount;
|
||||||
if ( bucket !== undefined ) {
|
this.blockFilterCount = selfie.blockFilterCount;
|
||||||
entry.next = bucket;
|
this.discardedCount = selfie.discardedCount;
|
||||||
}
|
for ( const [ catbits, bucket ] of selfie.categories ) {
|
||||||
this.dataFilters.set(entry.tokenHash, entry);
|
const tokenMap = new Map();
|
||||||
}
|
for ( const [ token, fdata ] of bucket ) {
|
||||||
|
tokenMap.set(token, filterFromCompiledData(fdata));
|
||||||
|
}
|
||||||
|
this.categories.set(catbits, tokenMap);
|
||||||
|
}
|
||||||
|
for ( const dataEntry of selfie.dataFilters ) {
|
||||||
|
const entry = FilterDataHolderEntry.load(dataEntry);
|
||||||
|
const bucket = this.dataFilters.get(entry.tokenHash);
|
||||||
|
if ( bucket !== undefined ) {
|
||||||
|
entry.next = bucket;
|
||||||
|
}
|
||||||
|
this.dataFilters.set(entry.tokenHash, entry);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
]).then(results =>
|
||||||
|
results.reduce((acc, v) => acc && v, true)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
let bytesInUse;
|
let bytesInUse;
|
||||||
let countdown = 0;
|
let countdown = 0;
|
||||||
|
|
||||||
let process = count => {
|
const process = count => {
|
||||||
if ( typeof count === 'number' ) {
|
if ( typeof count === 'number' ) {
|
||||||
if ( bytesInUse === undefined ) {
|
if ( bytesInUse === undefined ) {
|
||||||
bytesInUse = 0;
|
bytesInUse = 0;
|
||||||
|
@ -50,12 +50,11 @@
|
||||||
countdown += 1;
|
countdown += 1;
|
||||||
vAPI.storage.getBytesInUse(null, process);
|
vAPI.storage.getBytesInUse(null, process);
|
||||||
}
|
}
|
||||||
if (
|
if ( this.cacheStorage !== vAPI.storage ) {
|
||||||
this.cacheStorage !== vAPI.storage &&
|
|
||||||
this.cacheStorage.getBytesInUse instanceof Function
|
|
||||||
) {
|
|
||||||
countdown += 1;
|
countdown += 1;
|
||||||
this.cacheStorage.getBytesInUse(null, process);
|
this.assets.getBytesInUse().then(count => {
|
||||||
|
process(count);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if ( countdown === 0 ) {
|
if ( countdown === 0 ) {
|
||||||
callback();
|
callback();
|
||||||
|
@ -94,10 +93,10 @@
|
||||||
µBlock.loadHiddenSettings = function() {
|
µBlock.loadHiddenSettings = function() {
|
||||||
vAPI.storage.get('hiddenSettings', bin => {
|
vAPI.storage.get('hiddenSettings', bin => {
|
||||||
if ( bin instanceof Object === false ) { return; }
|
if ( bin instanceof Object === false ) { return; }
|
||||||
let hs = bin.hiddenSettings;
|
const hs = bin.hiddenSettings;
|
||||||
if ( hs instanceof Object ) {
|
if ( hs instanceof Object ) {
|
||||||
let hsDefault = this.hiddenSettingsDefault;
|
const hsDefault = this.hiddenSettingsDefault;
|
||||||
for ( let key in hsDefault ) {
|
for ( const key in hsDefault ) {
|
||||||
if (
|
if (
|
||||||
hsDefault.hasOwnProperty(key) &&
|
hsDefault.hasOwnProperty(key) &&
|
||||||
hs.hasOwnProperty(key) &&
|
hs.hasOwnProperty(key) &&
|
||||||
|
@ -110,6 +109,7 @@
|
||||||
if ( vAPI.localStorage.getItem('immediateHiddenSettings') === null ) {
|
if ( vAPI.localStorage.getItem('immediateHiddenSettings') === null ) {
|
||||||
this.saveImmediateHiddenSettings();
|
this.saveImmediateHiddenSettings();
|
||||||
}
|
}
|
||||||
|
self.log.verbosity = this.hiddenSettings.consoleLogLevel;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,8 +118,8 @@
|
||||||
// which were not modified by the user.
|
// which were not modified by the user.
|
||||||
|
|
||||||
µBlock.saveHiddenSettings = function(callback) {
|
µBlock.saveHiddenSettings = function(callback) {
|
||||||
let bin = { hiddenSettings: {} };
|
const bin = { hiddenSettings: {} };
|
||||||
for ( let prop in this.hiddenSettings ) {
|
for ( const prop in this.hiddenSettings ) {
|
||||||
if (
|
if (
|
||||||
this.hiddenSettings.hasOwnProperty(prop) &&
|
this.hiddenSettings.hasOwnProperty(prop) &&
|
||||||
this.hiddenSettings[prop] !== this.hiddenSettingsDefault[prop]
|
this.hiddenSettings[prop] !== this.hiddenSettingsDefault[prop]
|
||||||
|
@ -129,6 +129,7 @@
|
||||||
}
|
}
|
||||||
vAPI.storage.set(bin, callback);
|
vAPI.storage.set(bin, callback);
|
||||||
this.saveImmediateHiddenSettings();
|
this.saveImmediateHiddenSettings();
|
||||||
|
self.log.verbosity = this.hiddenSettings.consoleLogLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -969,41 +970,41 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.loadRedirectResources = function(updatedContent) {
|
µBlock.loadRedirectResources = function(updatedContent) {
|
||||||
var µb = this,
|
let content = '';
|
||||||
content = '';
|
|
||||||
|
|
||||||
var onDone = function() {
|
const onDone = ( ) => {
|
||||||
µb.redirectEngine.resourcesFromString(content);
|
this.redirectEngine.resourcesFromString(content);
|
||||||
};
|
};
|
||||||
|
|
||||||
var onUserResourcesLoaded = function(details) {
|
const onUserResourcesLoaded = details => {
|
||||||
if ( details.content !== '' ) {
|
if ( details.content !== '' ) {
|
||||||
content += '\n\n' + details.content;
|
content += '\n\n' + details.content;
|
||||||
}
|
}
|
||||||
onDone();
|
onDone();
|
||||||
};
|
};
|
||||||
|
|
||||||
var onResourcesLoaded = function(details) {
|
const onResourcesLoaded = details => {
|
||||||
if ( details.content !== '' ) {
|
if ( details.content !== '' ) {
|
||||||
content = details.content;
|
content = details.content;
|
||||||
}
|
}
|
||||||
if ( µb.hiddenSettings.userResourcesLocation === 'unset' ) {
|
if ( this.hiddenSettings.userResourcesLocation === 'unset' ) {
|
||||||
return onDone();
|
return onDone();
|
||||||
}
|
}
|
||||||
µb.assets.fetchText(µb.hiddenSettings.userResourcesLocation, onUserResourcesLoaded);
|
this.assets.fetchText(
|
||||||
|
this.hiddenSettings.userResourcesLocation,
|
||||||
|
onUserResourcesLoaded
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( typeof updatedContent === 'string' && updatedContent.length !== 0 ) {
|
if ( typeof updatedContent === 'string' && updatedContent.length !== 0 ) {
|
||||||
return onResourcesLoaded({ content: updatedContent });
|
return onResourcesLoaded({ content: updatedContent });
|
||||||
}
|
}
|
||||||
|
|
||||||
var onSelfieReady = function(success) {
|
this.redirectEngine.resourcesFromSelfie().then(success => {
|
||||||
if ( success !== true ) {
|
if ( success !== true ) {
|
||||||
µb.assets.get('ublock-resources', onResourcesLoaded);
|
this.assets.get('ublock-resources', onResourcesLoaded);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
µb.redirectEngine.resourcesFromSelfie(onSelfieReady);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -1013,39 +1014,25 @@
|
||||||
publicSuffixList.enableWASM();
|
publicSuffixList.enableWASM();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return this.assets.get(
|
||||||
// start of executor
|
'compiled/' + this.pslAssetKey
|
||||||
this.assets.get('compiled/' + this.pslAssetKey, details => {
|
).then(details =>
|
||||||
let selfie;
|
publicSuffixList.fromSelfie(details.content, µBlock.base128)
|
||||||
try {
|
).then(valid => {
|
||||||
selfie = JSON.parse(details.content);
|
if ( valid === true ) { return; }
|
||||||
} catch (ex) {
|
return this.assets.get(this.pslAssetKey, details => {
|
||||||
}
|
|
||||||
if (
|
|
||||||
selfie instanceof Object &&
|
|
||||||
publicSuffixList.fromSelfie(selfie)
|
|
||||||
) {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.assets.get(this.pslAssetKey, details => {
|
|
||||||
if ( details.content !== '' ) {
|
if ( details.content !== '' ) {
|
||||||
this.compilePublicSuffixList(details.content);
|
this.compilePublicSuffixList(details.content);
|
||||||
}
|
}
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// end of executor
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
µBlock.compilePublicSuffixList = function(content) {
|
µBlock.compilePublicSuffixList = function(content) {
|
||||||
publicSuffixList.parse(content, punycode.toASCII);
|
publicSuffixList.parse(content, punycode.toASCII);
|
||||||
this.assets.put(
|
this.assets.put(
|
||||||
'compiled/' + this.pslAssetKey,
|
'compiled/' + this.pslAssetKey,
|
||||||
JSON.stringify(publicSuffixList.toSelfie())
|
publicSuffixList.toSelfie(µBlock.base128)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1056,60 +1043,76 @@
|
||||||
// some set time.
|
// some set time.
|
||||||
|
|
||||||
µBlock.selfieManager = (function() {
|
µBlock.selfieManager = (function() {
|
||||||
let µb = µBlock;
|
const µb = µBlock;
|
||||||
let timer = null;
|
let timer;
|
||||||
|
|
||||||
// As of 2018-05-31:
|
// As of 2018-05-31:
|
||||||
// JSON.stringify-ing ourselves results in a better baseline
|
// JSON.stringify-ing ourselves results in a better baseline
|
||||||
// memory usage at selfie-load time. For some reasons.
|
// memory usage at selfie-load time. For some reasons.
|
||||||
|
|
||||||
let create = function() {
|
const create = function() {
|
||||||
timer = null;
|
Promise.all([
|
||||||
let selfie = JSON.stringify({
|
µb.assets.put(
|
||||||
magic: µb.systemSettings.selfieMagic,
|
'selfie/main',
|
||||||
availableFilterLists: µb.availableFilterLists,
|
JSON.stringify({
|
||||||
staticNetFilteringEngine: µb.staticNetFilteringEngine.toSelfie(),
|
magic: µb.systemSettings.selfieMagic,
|
||||||
redirectEngine: µb.redirectEngine.toSelfie(),
|
availableFilterLists: µb.availableFilterLists,
|
||||||
staticExtFilteringEngine: µb.staticExtFilteringEngine.toSelfie()
|
})
|
||||||
});
|
),
|
||||||
µb.cacheStorage.set({ selfie: selfie });
|
µb.redirectEngine.toSelfie('selfie/redirectEngine'),
|
||||||
µb.lz4Codec.relinquish();
|
µb.staticExtFilteringEngine.toSelfie('selfie/staticExtFilteringEngine'),
|
||||||
};
|
µb.staticNetFilteringEngine.toSelfie('selfie/staticNetFilteringEngine'),
|
||||||
|
]).then(( ) => {
|
||||||
let load = function(callback) {
|
µb.lz4Codec.relinquish();
|
||||||
µb.cacheStorage.get('selfie', function(bin) {
|
|
||||||
if (
|
|
||||||
bin instanceof Object === false ||
|
|
||||||
typeof bin.selfie !== 'string'
|
|
||||||
) {
|
|
||||||
return callback(false);
|
|
||||||
}
|
|
||||||
let selfie;
|
|
||||||
try {
|
|
||||||
selfie = JSON.parse(bin.selfie);
|
|
||||||
} catch(ex) {
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
selfie instanceof Object === false ||
|
|
||||||
selfie.magic !== µb.systemSettings.selfieMagic
|
|
||||||
) {
|
|
||||||
return callback(false);
|
|
||||||
}
|
|
||||||
µb.availableFilterLists = selfie.availableFilterLists;
|
|
||||||
µb.staticNetFilteringEngine.fromSelfie(selfie.staticNetFilteringEngine);
|
|
||||||
µb.redirectEngine.fromSelfie(selfie.redirectEngine);
|
|
||||||
µb.staticExtFilteringEngine.fromSelfie(selfie.staticExtFilteringEngine);
|
|
||||||
callback(true);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let destroy = function() {
|
const load = function() {
|
||||||
if ( timer !== null ) {
|
return Promise.all([
|
||||||
|
µb.assets.get('selfie/main').then(details => {
|
||||||
|
if (
|
||||||
|
details instanceof Object === false ||
|
||||||
|
typeof details.content !== 'string' ||
|
||||||
|
details.content === ''
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let selfie;
|
||||||
|
try {
|
||||||
|
selfie = JSON.parse(details.content);
|
||||||
|
} catch(ex) {
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
selfie instanceof Object === false ||
|
||||||
|
selfie.magic !== µb.systemSettings.selfieMagic
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
µb.availableFilterLists = selfie.availableFilterLists;
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
µb.redirectEngine.fromSelfie('selfie/redirectEngine'),
|
||||||
|
µb.staticExtFilteringEngine.fromSelfie('selfie/staticExtFilteringEngine'),
|
||||||
|
µb.staticNetFilteringEngine.fromSelfie('selfie/staticNetFilteringEngine'),
|
||||||
|
]).then(results =>
|
||||||
|
results.reduce((acc, v) => acc && v, true)
|
||||||
|
).catch(reason => {
|
||||||
|
log.info(reason);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const destroy = function() {
|
||||||
|
if ( timer !== undefined ) {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timer = null;
|
timer = undefined;
|
||||||
}
|
}
|
||||||
µb.cacheStorage.remove('selfie');
|
µb.cacheStorage.remove('selfie'); // TODO: obsolete, remove eventually.
|
||||||
timer = vAPI.setTimeout(create, µb.selfieAfter);
|
µb.assets.remove(/^selfie\//);
|
||||||
|
timer = vAPI.setTimeout(( ) => {
|
||||||
|
timer = undefined;
|
||||||
|
create();
|
||||||
|
}, µb.hiddenSettings.selfieAfter * 60000);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1299,6 +1302,8 @@
|
||||||
|
|
||||||
// Compile the list while we have the raw version in memory
|
// Compile the list while we have the raw version in memory
|
||||||
if ( topic === 'after-asset-updated' ) {
|
if ( topic === 'after-asset-updated' ) {
|
||||||
|
// Skip selfie-related content.
|
||||||
|
if ( details.assetKey.startsWith('selfie/') ) { return; }
|
||||||
var cached = typeof details.content === 'string' && details.content !== '';
|
var cached = typeof details.content === 'string' && details.content !== '';
|
||||||
if ( this.availableFilterLists.hasOwnProperty(details.assetKey) ) {
|
if ( this.availableFilterLists.hasOwnProperty(details.assetKey) ) {
|
||||||
if ( cached ) {
|
if ( cached ) {
|
||||||
|
@ -1334,8 +1339,8 @@
|
||||||
cached: cached
|
cached: cached
|
||||||
});
|
});
|
||||||
// https://github.com/gorhill/uBlock/issues/2585
|
// https://github.com/gorhill/uBlock/issues/2585
|
||||||
// Whenever an asset is overwritten, the current selfie is quite
|
// Whenever an asset is overwritten, the current selfie is quite
|
||||||
// likely no longer valid.
|
// likely no longer valid.
|
||||||
this.selfieManager.destroy();
|
this.selfieManager.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
109
src/js/utils.js
109
src/js/utils.js
|
@ -496,3 +496,112 @@
|
||||||
µBlock.orphanizeString = function(s) {
|
µBlock.orphanizeString = function(s) {
|
||||||
return JSON.parse(JSON.stringify(s));
|
return JSON.parse(JSON.stringify(s));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Custom base128 encoder/decoder
|
||||||
|
//
|
||||||
|
// TODO:
|
||||||
|
// Could expand the LZ4 codec API to be able to return UTF8-safe string
|
||||||
|
// representation of a compressed buffer, and thus the code below could be
|
||||||
|
// moved LZ4 codec-side.
|
||||||
|
|
||||||
|
µBlock.base128 = {
|
||||||
|
encode: function(arrbuf, arrlen) {
|
||||||
|
const inbuf = new Uint8Array(arrbuf, 0, arrlen);
|
||||||
|
const inputLength = arrlen;
|
||||||
|
let _7cnt = Math.floor(inputLength / 7);
|
||||||
|
let outputLength = _7cnt * 8;
|
||||||
|
let _7rem = inputLength % 7;
|
||||||
|
if ( _7rem !== 0 ) {
|
||||||
|
outputLength += 1 + _7rem;
|
||||||
|
}
|
||||||
|
const outbuf = new Uint8Array(outputLength);
|
||||||
|
let msbits, v;
|
||||||
|
let i = 0, j = 0;
|
||||||
|
while ( _7cnt-- ) {
|
||||||
|
v = inbuf[i+0];
|
||||||
|
msbits = (v & 0x80) >>> 7;
|
||||||
|
outbuf[j+1] = v & 0x7F;
|
||||||
|
v = inbuf[i+1];
|
||||||
|
msbits |= (v & 0x80) >>> 6;
|
||||||
|
outbuf[j+2] = v & 0x7F;
|
||||||
|
v = inbuf[i+2];
|
||||||
|
msbits |= (v & 0x80) >>> 5;
|
||||||
|
outbuf[j+3] = v & 0x7F;
|
||||||
|
v = inbuf[i+3];
|
||||||
|
msbits |= (v & 0x80) >>> 4;
|
||||||
|
outbuf[j+4] = v & 0x7F;
|
||||||
|
v = inbuf[i+4];
|
||||||
|
msbits |= (v & 0x80) >>> 3;
|
||||||
|
outbuf[j+5] = v & 0x7F;
|
||||||
|
v = inbuf[i+5];
|
||||||
|
msbits |= (v & 0x80) >>> 2;
|
||||||
|
outbuf[j+6] = v & 0x7F;
|
||||||
|
v = inbuf[i+6];
|
||||||
|
msbits |= (v & 0x80) >>> 1;
|
||||||
|
outbuf[j+7] = v & 0x7F;
|
||||||
|
outbuf[j+0] = msbits;
|
||||||
|
i += 7; j += 8;
|
||||||
|
}
|
||||||
|
if ( _7rem > 0 ) {
|
||||||
|
msbits = 0;
|
||||||
|
for ( let ir = 0; ir < _7rem; ir++ ) {
|
||||||
|
v = inbuf[i+ir];
|
||||||
|
msbits |= (v & 0x80) >>> (7 - ir);
|
||||||
|
outbuf[j+ir+1] = v & 0x7F;
|
||||||
|
}
|
||||||
|
outbuf[j+0] = msbits;
|
||||||
|
}
|
||||||
|
const textDecoder = new TextDecoder();
|
||||||
|
return textDecoder.decode(outbuf);
|
||||||
|
},
|
||||||
|
// TODO:
|
||||||
|
// Surprisingly, there does not seem to be any performance gain when
|
||||||
|
// first converting the input string into a Uint8Array through
|
||||||
|
// TextEncoder. Investigate again to confirm original findings and
|
||||||
|
// to find out whether results have changed. Not using TextEncoder()
|
||||||
|
// to create an intermediate input buffer lower peak memory usage
|
||||||
|
// at selfie load time.
|
||||||
|
//
|
||||||
|
// const textEncoder = new TextEncoder();
|
||||||
|
// const inbuf = textEncoder.encode(instr);
|
||||||
|
// const inputLength = inbuf.byteLength;
|
||||||
|
decode: function(instr, arrbuf) {
|
||||||
|
const inputLength = instr.length;
|
||||||
|
let _8cnt = inputLength >>> 3;
|
||||||
|
let outputLength = _8cnt * 7;
|
||||||
|
let _8rem = inputLength % 8;
|
||||||
|
if ( _8rem !== 0 ) {
|
||||||
|
outputLength += _8rem - 1;
|
||||||
|
}
|
||||||
|
const outbuf = arrbuf instanceof ArrayBuffer === false
|
||||||
|
? new Uint8Array(outputLength)
|
||||||
|
: new Uint8Array(arrbuf);
|
||||||
|
let msbits;
|
||||||
|
let i = 0, j = 0;
|
||||||
|
while ( _8cnt-- ) {
|
||||||
|
msbits = instr.charCodeAt(i+0);
|
||||||
|
outbuf[j+0] = msbits << 7 & 0x80 | instr.charCodeAt(i+1);
|
||||||
|
outbuf[j+1] = msbits << 6 & 0x80 | instr.charCodeAt(i+2);
|
||||||
|
outbuf[j+2] = msbits << 5 & 0x80 | instr.charCodeAt(i+3);
|
||||||
|
outbuf[j+3] = msbits << 4 & 0x80 | instr.charCodeAt(i+4);
|
||||||
|
outbuf[j+4] = msbits << 3 & 0x80 | instr.charCodeAt(i+5);
|
||||||
|
outbuf[j+5] = msbits << 2 & 0x80 | instr.charCodeAt(i+6);
|
||||||
|
outbuf[j+6] = msbits << 1 & 0x80 | instr.charCodeAt(i+7);
|
||||||
|
i += 8; j += 7;
|
||||||
|
}
|
||||||
|
if ( _8rem > 1 ) {
|
||||||
|
msbits = instr.charCodeAt(i+0);
|
||||||
|
for ( let ir = 1; ir < _8rem; ir++ ) {
|
||||||
|
outbuf[j+ir-1] = msbits << (8-ir) & 0x80 | instr.charCodeAt(i+ir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outbuf;
|
||||||
|
},
|
||||||
|
decodeSize: function(instr) {
|
||||||
|
const size = (instr.length >>> 3) * 7;
|
||||||
|
const rem = instr.length & 7;
|
||||||
|
return rem === 0 ? size : size + rem - 1;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue