mirror of
https://github.com/gorhill/uBlock.git
synced 2024-10-06 01:27:12 +02:00
Properly handle default list status changes in assets.json
This commit fix properly handling toggling off the default status of a list such that the list will be automatically turned off when its status change from default to non-default. Additionally, imported lists which become stock lists will be properly migrated from imported lists section.
This commit is contained in:
parent
6d989744bb
commit
2cd062898c
2
Makefile
2
Makefile
@ -4,7 +4,7 @@ run_options := $(filter-out $@,$(MAKECMDGOALS))
|
|||||||
.PHONY: all clean test lint chromium opera firefox npm dig mv3 mv3-quick \
|
.PHONY: all clean test lint chromium opera firefox npm dig mv3 mv3-quick \
|
||||||
compare maxcost medcost mincost modifiers record wasm
|
compare maxcost medcost mincost modifiers record wasm
|
||||||
|
|
||||||
sources := $(wildcard assets/resources/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*)
|
sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*)
|
||||||
platform := $(wildcard platform/* platform/*/* platform/*/*/* platform/*/*/*/*)
|
platform := $(wildcard platform/* platform/*/* platform/*/*/* platform/*/*/*/*)
|
||||||
assets := dist/build/uAssets
|
assets := dist/build/uAssets
|
||||||
|
|
||||||
|
@ -379,22 +379,21 @@ const getAssetSourceRegistry = function() {
|
|||||||
return assetSourceRegistryPromise;
|
return assetSourceRegistryPromise;
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerAssetSource = function(assetKey, dict) {
|
const registerAssetSource = function(assetKey, newDict) {
|
||||||
const entry = assetSourceRegistry[assetKey] || {};
|
const currentDict = assetSourceRegistry[assetKey] || {};
|
||||||
for ( const prop in dict ) {
|
for ( const [ k, v ] of Object.entries(newDict) ) {
|
||||||
if ( dict.hasOwnProperty(prop) === false ) { continue; }
|
if ( v === undefined || v === null ) {
|
||||||
if ( dict[prop] === undefined ) {
|
delete currentDict[k];
|
||||||
delete entry[prop];
|
|
||||||
} else {
|
} else {
|
||||||
entry[prop] = dict[prop];
|
currentDict[k] = newDict[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let contentURL = dict.contentURL;
|
let contentURL = newDict.contentURL;
|
||||||
if ( contentURL !== undefined ) {
|
if ( contentURL !== undefined ) {
|
||||||
if ( typeof contentURL === 'string' ) {
|
if ( typeof contentURL === 'string' ) {
|
||||||
contentURL = entry.contentURL = [ contentURL ];
|
contentURL = currentDict.contentURL = [ contentURL ];
|
||||||
} else if ( Array.isArray(contentURL) === false ) {
|
} else if ( Array.isArray(contentURL) === false ) {
|
||||||
contentURL = entry.contentURL = [];
|
contentURL = currentDict.contentURL = [];
|
||||||
}
|
}
|
||||||
let remoteURLCount = 0;
|
let remoteURLCount = 0;
|
||||||
for ( let i = 0; i < contentURL.length; i++ ) {
|
for ( let i = 0; i < contentURL.length; i++ ) {
|
||||||
@ -402,18 +401,18 @@ const registerAssetSource = function(assetKey, dict) {
|
|||||||
remoteURLCount += 1;
|
remoteURLCount += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entry.hasLocalURL = remoteURLCount !== contentURL.length;
|
currentDict.hasLocalURL = remoteURLCount !== contentURL.length;
|
||||||
entry.hasRemoteURL = remoteURLCount !== 0;
|
currentDict.hasRemoteURL = remoteURLCount !== 0;
|
||||||
} else if ( entry.contentURL === undefined ) {
|
} else if ( currentDict.contentURL === undefined ) {
|
||||||
entry.contentURL = [];
|
currentDict.contentURL = [];
|
||||||
}
|
}
|
||||||
if ( typeof entry.updateAfter !== 'number' ) {
|
if ( typeof currentDict.updateAfter !== 'number' ) {
|
||||||
entry.updateAfter = 5;
|
currentDict.updateAfter = 5;
|
||||||
}
|
}
|
||||||
if ( entry.submitter ) {
|
if ( currentDict.submitter ) {
|
||||||
entry.submitTime = Date.now(); // To detect stale entries
|
currentDict.submitTime = Date.now(); // To detect stale entries
|
||||||
}
|
}
|
||||||
assetSourceRegistry[assetKey] = entry;
|
assetSourceRegistry[assetKey] = currentDict;
|
||||||
};
|
};
|
||||||
|
|
||||||
const unregisterAssetSource = function(assetKey) {
|
const unregisterAssetSource = function(assetKey) {
|
||||||
@ -443,12 +442,18 @@ const updateAssetSourceRegistry = function(json, silent = false) {
|
|||||||
let newDict;
|
let newDict;
|
||||||
try {
|
try {
|
||||||
newDict = JSON.parse(json);
|
newDict = JSON.parse(json);
|
||||||
|
newDict['assets.json'].defaultListset =
|
||||||
|
Array.from(Object.entries(newDict))
|
||||||
|
.filter(a => a[1].content === 'filters' && a[1].off === undefined)
|
||||||
|
.map(a => a[0]);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
}
|
}
|
||||||
if ( newDict instanceof Object === false ) { return; }
|
if ( newDict instanceof Object === false ) { return; }
|
||||||
|
|
||||||
const oldDict = assetSourceRegistry;
|
const oldDict = assetSourceRegistry;
|
||||||
|
|
||||||
|
fireNotification('assets.json-updated', { newDict, oldDict });
|
||||||
|
|
||||||
// Remove obsolete entries (only those which were built-in).
|
// Remove obsolete entries (only those which were built-in).
|
||||||
for ( const assetKey in oldDict ) {
|
for ( const assetKey in oldDict ) {
|
||||||
if (
|
if (
|
||||||
@ -1005,7 +1010,16 @@ const updateNext = async function() {
|
|||||||
// In auto-update context, be gentle on remote servers.
|
// In auto-update context, be gentle on remote servers.
|
||||||
remoteServerFriendly = updaterAuto;
|
remoteServerFriendly = updaterAuto;
|
||||||
|
|
||||||
const result = await getRemote(toUpdate[0]);
|
let result;
|
||||||
|
if (
|
||||||
|
toUpdate[0] !== 'assets.json' ||
|
||||||
|
µb.hiddenSettings.debugAssetsJson !== true
|
||||||
|
) {
|
||||||
|
result = await getRemote(toUpdate[0]);
|
||||||
|
} else {
|
||||||
|
result = await assets.fetchText('/assets/assets.json');
|
||||||
|
result.assetKey = 'assets.json';
|
||||||
|
}
|
||||||
|
|
||||||
remoteServerFriendly = false;
|
remoteServerFriendly = false;
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ const hiddenSettingsDefault = {
|
|||||||
cnameReplayFullURL: false,
|
cnameReplayFullURL: false,
|
||||||
cnameUncloakProxied: false,
|
cnameUncloakProxied: false,
|
||||||
consoleLogLevel: 'unset',
|
consoleLogLevel: 'unset',
|
||||||
|
debugAssetsJson: false,
|
||||||
debugScriptlets: false,
|
debugScriptlets: false,
|
||||||
debugScriptletInjector: false,
|
debugScriptletInjector: false,
|
||||||
disableWebAssembly: false,
|
disableWebAssembly: false,
|
||||||
|
@ -649,22 +649,21 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µb.getAvailableLists = async function() {
|
µb.getAvailableLists = async function() {
|
||||||
let oldAvailableLists = {},
|
const newAvailableLists = {};
|
||||||
newAvailableLists = {};
|
|
||||||
|
|
||||||
// User filter list.
|
// User filter list
|
||||||
newAvailableLists[this.userFiltersPath] = {
|
newAvailableLists[this.userFiltersPath] = {
|
||||||
content: 'filters',
|
content: 'filters',
|
||||||
group: 'user',
|
group: 'user',
|
||||||
title: i18n$('1pPageName'),
|
title: i18n$('1pPageName'),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Custom filter lists.
|
// Custom filter lists
|
||||||
const importedListKeys = this.listKeysFromCustomFilterLists(
|
const importedListKeys = new Set(
|
||||||
this.userSettings.importedLists
|
this.listKeysFromCustomFilterLists(this.userSettings.importedLists)
|
||||||
);
|
);
|
||||||
for ( const listKey of importedListKeys ) {
|
for ( const listKey of importedListKeys ) {
|
||||||
const entry = {
|
const asset = {
|
||||||
content: 'filters',
|
content: 'filters',
|
||||||
contentURL: listKey,
|
contentURL: listKey,
|
||||||
external: true,
|
external: true,
|
||||||
@ -672,45 +671,17 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
|||||||
submitter: 'user',
|
submitter: 'user',
|
||||||
title: '',
|
title: '',
|
||||||
};
|
};
|
||||||
newAvailableLists[listKey] = entry;
|
newAvailableLists[listKey] = asset;
|
||||||
io.registerAssetSource(listKey, entry);
|
io.registerAssetSource(listKey, asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a no longer existing stock list into an imported list, except
|
// Load previously saved available lists -- these contains data
|
||||||
// when the removed stock list is deemed a "bad list".
|
// computed at run-time, we will reuse this data if possible
|
||||||
const customListFromStockList = assetKey => {
|
const [ bin, registeredAssets, badlists ] = await Promise.all([
|
||||||
const oldEntry = oldAvailableLists[assetKey];
|
|
||||||
if ( oldEntry === undefined || oldEntry.off === true ) { return; }
|
|
||||||
let listURL = oldEntry.contentURL;
|
|
||||||
if ( Array.isArray(listURL) ) {
|
|
||||||
listURL = listURL[0];
|
|
||||||
}
|
|
||||||
if ( this.badLists.has(listURL) ) { return; }
|
|
||||||
const newEntry = {
|
|
||||||
content: 'filters',
|
|
||||||
contentURL: listURL,
|
|
||||||
external: true,
|
|
||||||
group: 'custom',
|
|
||||||
submitter: 'user',
|
|
||||||
title: oldEntry.title || ''
|
|
||||||
};
|
|
||||||
newAvailableLists[listURL] = newEntry;
|
|
||||||
io.registerAssetSource(listURL, newEntry);
|
|
||||||
importedListKeys.push(listURL);
|
|
||||||
this.userSettings.importedLists.push(listURL.trim());
|
|
||||||
this.saveUserSettings();
|
|
||||||
this.saveSelectedFilterLists([ listURL ], true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const promises = [
|
|
||||||
vAPI.storage.get('availableFilterLists'),
|
vAPI.storage.get('availableFilterLists'),
|
||||||
io.metadata(),
|
io.metadata(),
|
||||||
this.badLists.size === 0 ? io.get('ublock-badlists') : false,
|
this.badLists.size === 0 ? io.get('ublock-badlists') : false,
|
||||||
];
|
]);
|
||||||
|
|
||||||
// Load previously saved available lists -- these contains data
|
|
||||||
// computed at run-time, we will reuse this data if possible.
|
|
||||||
const [ bin, entries, badlists ] = await Promise.all(promises);
|
|
||||||
|
|
||||||
if ( badlists instanceof Object ) {
|
if ( badlists instanceof Object ) {
|
||||||
for ( const line of badlists.content.split(/\s*[\n\r]+\s*/) ) {
|
for ( const line of badlists.content.split(/\s*[\n\r]+\s*/) ) {
|
||||||
@ -721,51 +692,97 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oldAvailableLists = bin && bin.availableFilterLists || {};
|
const oldAvailableLists = bin && bin.availableFilterLists || {};
|
||||||
|
|
||||||
for ( const assetKey in entries ) {
|
for ( const [ assetKey, asset ] of Object.entries(registeredAssets) ) {
|
||||||
if ( entries.hasOwnProperty(assetKey) === false ) { continue; }
|
if ( asset.content !== 'filters' ) { continue; }
|
||||||
const entry = entries[assetKey];
|
newAvailableLists[assetKey] = Object.assign({}, asset);
|
||||||
if ( entry.content !== 'filters' ) { continue; }
|
|
||||||
newAvailableLists[assetKey] = Object.assign({}, entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load set of currently selected filter lists.
|
// Load set of currently selected filter lists
|
||||||
const listKeySet = new Set(this.selectedFilterLists);
|
const selectedListset = new Set(this.selectedFilterLists);
|
||||||
for ( const listKey in newAvailableLists ) {
|
|
||||||
if ( newAvailableLists.hasOwnProperty(listKey) ) {
|
|
||||||
newAvailableLists[listKey].off = !listKeySet.has(listKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//finalize();
|
// Remove imported filter lists which are already present in stock lists
|
||||||
// Final steps:
|
for ( const [ stockAssetKey, stockEntry ] of Object.entries(newAvailableLists) ) {
|
||||||
// - reuse existing list metadata if any;
|
if ( stockEntry.content !== 'filters' ) { continue; }
|
||||||
// - unregister unreferenced imported filter lists if any.
|
if ( stockEntry.group === 'user' ) { continue; }
|
||||||
// Reuse existing metadata.
|
if ( stockEntry.submitter === 'user' ) { continue; }
|
||||||
for ( const assetKey in oldAvailableLists ) {
|
if ( stockAssetKey.includes('://') ) { continue; }
|
||||||
const oldEntry = oldAvailableLists[assetKey];
|
const contentURLs = Array.isArray(stockEntry.contentURL)
|
||||||
const newEntry = newAvailableLists[assetKey];
|
? stockEntry.contentURL
|
||||||
// List no longer exists. If a stock list, try to convert to
|
: [ stockEntry.contentURL ];
|
||||||
// imported list if it was selected.
|
for ( const importedAssetKey of contentURLs ) {
|
||||||
if ( newEntry === undefined ) {
|
const importedEntry = newAvailableLists[importedAssetKey];
|
||||||
this.removeFilterList(assetKey);
|
if ( importedEntry === undefined ) { continue; }
|
||||||
if ( assetKey.indexOf('://') === -1 ) {
|
delete newAvailableLists[importedAssetKey];
|
||||||
customListFromStockList(assetKey);
|
io.unregisterAssetSource(importedAssetKey);
|
||||||
|
this.removeFilterList(importedAssetKey);
|
||||||
|
if ( selectedListset.has(importedAssetKey) ) {
|
||||||
|
selectedListset.add(stockAssetKey);
|
||||||
|
selectedListset.delete(importedAssetKey);
|
||||||
}
|
}
|
||||||
continue;
|
importedListKeys.delete(importedAssetKey);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister lists in old listset not present in new listset.
|
||||||
|
// Convert a no longer existing stock list into an imported list, except
|
||||||
|
// when the removed stock list is deemed a "bad list".
|
||||||
|
for ( const [ assetKey, oldEntry ] of Object.entries(oldAvailableLists) ) {
|
||||||
|
if ( newAvailableLists[assetKey] !== undefined ) { continue; }
|
||||||
|
const on = selectedListset.delete(assetKey);
|
||||||
|
this.removeFilterList(assetKey);
|
||||||
|
io.unregisterAssetSource(assetKey);
|
||||||
|
if ( assetKey.includes('://') ) { continue; }
|
||||||
|
if ( on === false ) { continue; }
|
||||||
|
const listURL = Array.isArray(oldEntry.contentURL)
|
||||||
|
? oldEntry.contentURL[0]
|
||||||
|
: oldEntry.contentURL;
|
||||||
|
if ( this.badLists.has(listURL) ) { continue; }
|
||||||
|
const newEntry = {
|
||||||
|
content: 'filters',
|
||||||
|
contentURL: listURL,
|
||||||
|
external: true,
|
||||||
|
group: 'custom',
|
||||||
|
submitter: 'user',
|
||||||
|
title: oldEntry.title || ''
|
||||||
|
};
|
||||||
|
newAvailableLists[listURL] = newEntry;
|
||||||
|
io.registerAssetSource(listURL, newEntry);
|
||||||
|
importedListKeys.add(listURL);
|
||||||
|
selectedListset.add(listURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove unreferenced imported filter lists
|
||||||
|
for ( const [ assetKey, asset ] of Object.entries(newAvailableLists) ) {
|
||||||
|
if ( asset.submitter !== 'user' ) { continue; }
|
||||||
|
if ( importedListKeys.has(assetKey) ) { continue; }
|
||||||
|
selectedListset.delete(assetKey);
|
||||||
|
delete newAvailableLists[assetKey];
|
||||||
|
this.removeFilterList(assetKey);
|
||||||
|
io.unregisterAssetSource(assetKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark lists as disabled/enabled according to selected listset
|
||||||
|
for ( const [ assetKey, asset ] of Object.entries(newAvailableLists) ) {
|
||||||
|
asset.off = selectedListset.has(assetKey) === false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reuse existing metadata
|
||||||
|
for ( const [ assetKey, oldEntry ] of Object.entries(oldAvailableLists) ) {
|
||||||
|
const newEntry = newAvailableLists[assetKey];
|
||||||
|
if ( newEntry === undefined ) { continue; }
|
||||||
if ( oldEntry.entryCount !== undefined ) {
|
if ( oldEntry.entryCount !== undefined ) {
|
||||||
newEntry.entryCount = oldEntry.entryCount;
|
newEntry.entryCount = oldEntry.entryCount;
|
||||||
}
|
}
|
||||||
if ( oldEntry.entryUsedCount !== undefined ) {
|
if ( oldEntry.entryUsedCount !== undefined ) {
|
||||||
newEntry.entryUsedCount = oldEntry.entryUsedCount;
|
newEntry.entryUsedCount = oldEntry.entryUsedCount;
|
||||||
}
|
}
|
||||||
// This may happen if the list name was pulled from the list
|
// This may happen if the list name was pulled from the list content
|
||||||
// content.
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/982
|
// https://github.com/chrisaljoudi/uBlock/issues/982
|
||||||
// There is no guarantee the title was successfully extracted from
|
// There is no guarantee the title was successfully extracted from
|
||||||
// the list content.
|
// the list content
|
||||||
if (
|
if (
|
||||||
newEntry.title === '' &&
|
newEntry.title === '' &&
|
||||||
typeof oldEntry.title === 'string' &&
|
typeof oldEntry.title === 'string' &&
|
||||||
@ -775,14 +792,13 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove unreferenced imported filter lists.
|
if ( Array.from(importedListKeys).join('\n') !== this.userSettings.importedLists.join('\n') ) {
|
||||||
for ( const assetKey in newAvailableLists ) {
|
this.userSettings.importedLists = Array.from(importedListKeys);
|
||||||
const newEntry = newAvailableLists[assetKey];
|
this.saveUserSettings();
|
||||||
if ( newEntry.submitter !== 'user' ) { continue; }
|
}
|
||||||
if ( importedListKeys.indexOf(assetKey) !== -1 ) { continue; }
|
|
||||||
delete newAvailableLists[assetKey];
|
if ( Array.from(selectedListset).join() !== this.selectedFilterLists.join() ) {
|
||||||
io.unregisterAssetSource(assetKey);
|
this.saveSelectedFilterLists(Array.from(selectedListset));
|
||||||
this.removeFilterList(assetKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newAvailableLists;
|
return newAvailableLists;
|
||||||
@ -1580,7 +1596,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
|||||||
if ( topic === 'builtin-asset-source-added' ) {
|
if ( topic === 'builtin-asset-source-added' ) {
|
||||||
if ( details.entry.content === 'filters' ) {
|
if ( details.entry.content === 'filters' ) {
|
||||||
if (
|
if (
|
||||||
details.entry.off !== true ||
|
details.entry.off === true &&
|
||||||
this.listMatchesEnvironment(details.entry)
|
this.listMatchesEnvironment(details.entry)
|
||||||
) {
|
) {
|
||||||
this.saveSelectedFilterLists([ details.assetKey ], true);
|
this.saveSelectedFilterLists([ details.assetKey ], true);
|
||||||
@ -1588,4 +1604,32 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( topic === 'assets.json-updated' ) {
|
||||||
|
const { newDict, oldDict } = details;
|
||||||
|
const newDefaultListset = new Set(newDict['assets.json'].defaultListset || []);
|
||||||
|
const oldDefaultListset = new Set(oldDict['assets.json'].defaultListset || []);
|
||||||
|
if ( oldDefaultListset.size === 0 ) {
|
||||||
|
Array.from(Object.entries(newDict))
|
||||||
|
.filter(a => a[1].content === 'filters' && a[1].off === undefined)
|
||||||
|
.map(a => a[0])
|
||||||
|
.forEach(a => oldDefaultListset.add(a));
|
||||||
|
}
|
||||||
|
const selectedListset = new Set(this.selectedFilterLists);
|
||||||
|
let selectedListModified = false;
|
||||||
|
for ( const assetKey of oldDefaultListset ) {
|
||||||
|
if ( newDefaultListset.has(assetKey) ) { continue; }
|
||||||
|
selectedListset.delete(assetKey);
|
||||||
|
selectedListModified = true;
|
||||||
|
}
|
||||||
|
for ( const assetKey of newDefaultListset ) {
|
||||||
|
if ( oldDefaultListset.has(assetKey) ) { continue; }
|
||||||
|
selectedListset.add(assetKey);
|
||||||
|
selectedListModified = true;
|
||||||
|
}
|
||||||
|
if ( selectedListModified ) {
|
||||||
|
this.saveSelectedFilterLists(Array.from(selectedListset));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user