3rd-party filters pane revisited

This commit is contained in:
gorhill 2017-01-22 16:05:16 -05:00
parent 20ebe6d18f
commit 9309df4196
10 changed files with 394 additions and 282 deletions

View File

@ -30,7 +30,8 @@
"contentURL": [
"https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt",
"assets/ublock/filters.txt"
]
],
"supportURL": "https://github.com/uBlockOrigin/uAssets/issues"
},
"ublock-badware": {
"content": "filters",
@ -196,7 +197,11 @@
"group": "social",
"off": true,
"title": "Anti-ThirdpartySocial (see warning inside list)",
"contentURL": "https://www.fanboy.co.nz/fanboy-antifacebook.txt",
"contentURL": [
"https://easylist.to/easylist/fanboy-social.txt",
"https://fanboy.co.nz/fanboy-antifacebook.txt",
"https://easylist-downloads.adblockplus.org/fanboy-social.txt"
],
"supportURL": "https://forums.lanik.us/"
},
"fanboy-annoyance": {
@ -206,6 +211,7 @@
"title": "Fanboys Annoyance List",
"contentURL": [
"https://easylist.to/easylist/fanboy-annoyance.txt",
"https://fanboy.co.nz/fanboy-annoyance.txt",
"https://easylist-downloads.adblockplus.org/fanboy-annoyance.txt"
],
"supportURL": "https://forums.lanik.us/"
@ -217,6 +223,7 @@
"title": "Fanboys Social Blocking List",
"contentURL": [
"https://easylist.to/easylist/fanboy-social.txt",
"https://fanboy.co.nz/fanboy-social.txt",
"https://easylist-downloads.adblockplus.org/fanboy-social.txt"
],
"supportURL": "https://forums.lanik.us/"

View File

@ -21,24 +21,19 @@
<button id="buttonPurgeAll" class="custom disabled" data-i18n="3pPurgeAll"></button>
<li><input type="checkbox" id="parseCosmeticFilters"><label data-i18n="3pParseAllABPHideFiltersPrompt1" for="parseCosmeticFilters"></label><button class="whatisthis"></button>
<div class="whatisthis-expandable para" data-i18n="3pParseAllABPHideFiltersInfo"></div>
<ul style="padding-left: 2em;">
<li><input type="checkbox" id="ignoreGenericCosmeticFilters"><label data-i18n="3pIgnoreGenericCosmeticFilters" for="ignoreGenericCosmeticFilters"></label><button class="whatisthis"></button>
<div class="whatisthis-expandable para" data-i18n="3pIgnoreGenericCosmeticFiltersInfo"></div>
</ul>
<li><p id="listsOfBlockedHostsPrompt"></p>
<li><input type="checkbox" id="ignoreGenericCosmeticFilters"><label data-i18n="3pIgnoreGenericCosmeticFilters" for="ignoreGenericCosmeticFilters"></label><button class="whatisthis"></button>
<div class="whatisthis-expandable para" data-i18n="3pIgnoreGenericCosmeticFiltersInfo"></div>
</ul>
<p><span id="listsOfBlockedHostsPrompt"></span></p>
<ul id="lists"></ul>
</div>
<div id="externalListsDiv">
<p>
<span data-i18n="3pExternalListsHint" style="margin: 0 0 0.25em 0; font-size: 13px;"></span>
<a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Filter-lists-from-around-the-web" target="_blank">&#xf05a;</a>
</p>
<p style="margin: 0.25em 0 0 0">
<textarea id="externalLists" dir="ltr" spellcheck="false"></textarea>
<button id="externalListsApply" class="custom important" disabled="true" data-i18n="3pExternalListsApply"></button></p>
</div>
<p>
<span data-i18n="3pExternalListsHint" style="margin: 0 0 0.25em 0; font-size: 13px;"></span>
<a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Filter-lists-from-around-the-web" target="_blank">&#xf05a;</a>
<textarea id="externalLists" dir="ltr" spellcheck="false"></textarea>
</div>
<div id="templates" style="display: none;">
<ul>
@ -46,14 +41,16 @@
<ul class="listEntries"></ul>
</li>
<li class="listEntry">
<input type="checkbox">
<a type="text/plain" target="_blank" href=""></a>
<a class="fa" style="display: none;" target="_blank">&#xf05a;</a>
<a href="" style="display: none;" target="_blank"></a>: <!--
<input type="checkbox"><!--
--><a class="content" type="text/plain" target="_blank" href=""></a><!--
--><a class="fa support" href="" target="_blank">&#xf015;</a><!--
--><a class="fa remove" href="">&#xf00d;</a><!--
--><a class="fa mustread" href="" target="_blank">&#xf05a;</a><!--
--><span class="fa status unsecure" title="http">&#xf13e;</span><!--
--><span class="counts dim"></span><!--
--><span class="status unsecure">http</span><!--
--><span class="status obsolete" data-i18n="3pExternalListObsolete"></span><!--
--><span class="status purge" data-i18n="3pExternalListPurge"></span><!--
--><span class="fa status failed">&#xf06a;</span><!--
--><span class="fa status obsolete" title="3pExternalListObsolete">&#xf071;</span><!--
--><span class="fa status cache">&#xf017;</span><!--
--><span class="fa status updating">&#xf110;</span>
</li>
</ul>

View File

@ -9,14 +9,24 @@ ul {
#options li {
margin-bottom: 0.5em;
}
#listsOfBlockedHostsPrompt {
cursor: pointer;
}
#listsOfBlockedHostsPrompt:before {
color: #aaa;
content: '\2212 ';
}
body.hideUnused #listsOfBlockedHostsPrompt:before {
content: '+ ';
}
#lists {
margin: 0.5em 0 0 0;
padding-left: 1em;
padding-left: 0.5em;
padding-right: 0em;
}
body[dir=rtl] #lists {
padding-left: 0em;
padding-right: 1em;
padding-right: 0.5em;
}
#lists > li {
margin: 0.5em 0 0 0;
@ -45,8 +55,9 @@ body[dir=rtl] #lists {
display: none;
}
li.listEntry {
line-height: 150%;
margin: 0 auto 0 auto;
margin-left: 3em;
margin-left: 2.5em;
margin-right: 0em;
text-indent: -2em;
}
@ -55,26 +66,51 @@ body[dir=rtl] li.listEntry {
margin-right: 1em;
}
li.listEntry > * {
margin-right: 0.5em;
text-indent: 0;
unicode-bidi: embed;
}
li.listEntry > a:nth-of-type(2) {
font-size: 16px;
opacity: 0.7;
li.listEntry.toRemove > input[type="checkbox"] {
visibility: hidden;
}
li.listEntry > a:nth-of-type(2),
li.listEntry > a:nth-of-type(2):visited {
color: mediumblue;
li.listEntry.toRemove > a.content {
text-decoration: line-through;
}
li.listEntry > a:nth-of-type(2):hover {
li.listEntry > .fa {
color: inherit;
display: none;
font-size: 110%;
opacity: 0.5;
vertical-align: baseline;
}
li.listEntry > a.fa:hover {
opacity: 1;
}
li.listEntry > a:nth-of-type(3) {
font-size: smaller;
opacity: 0.5;
li.listEntry.support > a.support {
display: inline-block;
}
li.listEntry > a.remove,
li.listEntry > a.remove:visited {
color: darkred;
}
li.listEntry.external > a.remove {
display: inline-block;
}
li.listEntry.mustread > a.mustread {
display: inline-block;
}
li.listEntry.mustread > a.mustread:hover {
color: mediumblue;
}
li.listEntry > .counts {
display: none;
font-size: smaller;
}
li.listEntry > input[type="checkbox"]:checked ~ .counts {
display: inline;
}
.dim {
opacity: 0.5;
opacity: 0.6;
}
#buttonApply {
display: initial;
@ -89,42 +125,42 @@ body[dir=rtl] #buttonApply {
#buttonApply.disabled {
display: none;
}
span.status {
border: 1px solid transparent;
li.listEntry span.status {
color: #444;
cursor: default;
display: none;
font-size: smaller;
line-height: 1;
margin: 0 0 0 0.5em;
opacity: 0.8;
padding: 1px 2px;
}
span.unsecure {
background-color: hsl(0, 100%, 88%);
border-color: hsl(0, 100%, 83%);
}
li.listEntry.unsecure span.unsecure {
display: inline;
}
span.obsolete {
background-color: hsl(36, 100%, 80%);
border-color: hsl(36, 100%, 75%);
}
li.listEntry.obsolete > input[type="checkbox"]:checked ~ span.obsolete {
display: inline;
}
span.purge {
border-color: #ddd;
background-color: #eee;
cursor: pointer;
}
span.purge:hover {
li.listEntry span.status:hover {
opacity: 1;
}
li.listEntry.cached span.purge {
display: inline;
li.listEntry span.status.fa {
/* font-size: 110%; */
}
span.updating {
li.listEntry span.unsecure {
color: darkred;
}
li.listEntry.unsecure > input[type="checkbox"]:checked ~ span.unsecure {
display: inline-block;
}
li.listEntry span.failed {
color: darkred;
}
li.listEntry.failed span.failed {
display: inline-block;
}
li.listEntry span.cache {
cursor: pointer;
}
li.listEntry.cached:not(.updating):not(.obsolete) > input[type="checkbox"]:checked ~ span.cache {
display: inline-block;
}
li.listEntry span.obsolete {
color: hsl(36, 100%, 40%);
}
li.listEntry.obsolete:not(.updating) > input[type="checkbox"]:checked ~ span.obsolete {
display: inline-block;
}
li.listEntry span.updating {
border: none;
padding: 0;
}
@ -133,14 +169,15 @@ li.listEntry.updating span.updating {
display: inline-block;
}
#externalListsDiv {
margin: 2em auto 0 2em;
margin: 1.5em auto 0 1.5em;
}
body[dir=rtl] #externalListsDiv {
margin: 2em 2em 0;
margin: 1.5em 1.5em 0 auto;
}
#externalLists {
box-sizing: border-box;
height: 10em;
height: 8em;
margin-top: 0.25em;
white-space: pre;
width: 100%;
word-wrap: normal;

View File

@ -31,7 +31,7 @@
var listDetails = {},
filteringSettingsHash = '',
externalLists = '';
lastUpdateTemplateString = vAPI.i18n('3pLastUpdate');
/******************************************************************************/
@ -41,10 +41,6 @@ var onMessage = function(msg) {
updateAssetStatus(msg);
break;
case 'staticFilteringDataChanged':
filteringSettingsHash = [
msg.parseCosmeticFilters,
msg.ignoreGenericCosmeticFilters
].concat(msg.listKeys.sort()).join();
renderFilterLists();
break;
default:
@ -63,12 +59,12 @@ var renderNumber = function(value) {
/******************************************************************************/
var renderFilterLists = function(first) {
var renderFilterLists = function(soft) {
var listGroupTemplate = uDom('#templates .groupEntry'),
listEntryTemplate = uDom('#templates .listEntry'),
listStatsTemplate = vAPI.i18n('3pListsOfBlockedHostsPerListStats'),
renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString,
lastUpdateString = vAPI.i18n('3pLastUpdate');
hideUnusedLists = document.body.classList.contains('hideUnused');
// Assemble a pretty list name if possible
var listNameFromListKey = function(listKey) {
@ -92,46 +88,55 @@ var renderFilterLists = function(first) {
elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey));
elem.setAttribute('type', 'text/html');
elem.textContent = listNameFromListKey(listKey) + '\u200E';
elem = li.querySelector('a:nth-of-type(2)');
if ( entry.instructionURL ) {
elem.setAttribute('href', entry.instructionURL);
elem.style.setProperty('display', '');
} else {
elem.style.setProperty('display', 'none');
}
elem = li.querySelector('a:nth-of-type(3)');
if ( entry.supportName ) {
li.classList.add('support');
elem = li.querySelector('a.support');
elem.setAttribute('href', entry.supportURL);
elem.textContent = '(' + entry.supportName + ')';
elem.style.setProperty('display', '');
elem.setAttribute('title', entry.supportName);
} else {
elem.style.setProperty('display', 'none');
li.classList.remove('support');
}
if ( entry.external ) {
li.classList.add('external');
} else {
li.classList.remove('external');
}
if ( entry.instructionURL ) {
li.classList.add('mustread');
elem = li.querySelector('a.mustread');
elem.setAttribute('href', entry.instructionURL);
} else {
li.classList.remove('mustread');
}
}
// https://github.com/gorhill/uBlock/issues/1429
if ( !soft ) {
elem = li.querySelector('input[type="checkbox"]');
elem.checked = entry.off !== true;
}
li.style.setProperty('display', hideUnusedLists && entry.off === true ? 'none' : '');
elem = li.querySelector('span.counts');
var text = listStatsTemplate
.replace('{{used}}', renderNumber(!entry.off && !isNaN(+entry.entryUsedCount) ? entry.entryUsedCount : 0))
.replace('{{total}}', !isNaN(+entry.entryCount) ? renderNumber(entry.entryCount) : '?');
var text = '';
if ( !isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount) ) {
text = listStatsTemplate
.replace('{{used}}', renderNumber(entry.off ? 0 : entry.entryUsedCount))
.replace('{{total}}', renderNumber(entry.entryCount));
}
elem.textContent = text;
// https://github.com/chrisaljoudi/uBlock/issues/104
var asset = listDetails.cache[listKey] || {};
// https://github.com/gorhill/uBlock/issues/78
// Badge for non-secure connection
var remoteURL = asset.remoteURL;
li.classList.toggle(
'unsecure',
typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0
);
// Badge for update status
li.classList.toggle('obsolete', entry.off !== true && asset.obsolete === true);
// Badge for cache status
li.classList.toggle('failed', asset.error !== undefined);
li.classList.toggle('obsolete', asset.obsolete === true);
li.classList.toggle('cached', asset.cached === true && asset.writeTime > 0);
if ( asset.cached ) {
li.querySelector('.status.purge').setAttribute(
li.querySelector('.status.cache').setAttribute(
'title',
lastUpdateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime))
lastUpdateTemplateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime))
);
}
li.classList.remove('updating');
@ -237,7 +242,6 @@ var renderFilterLists = function(first) {
}
uDom('#lists .listEntries .listEntry.discard').remove();
uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete') === null);
uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
uDom('#listsOfBlockedHostsPrompt').text(
vAPI.i18n('3pListsOfBlockedHostsPrompt')
@ -247,9 +251,9 @@ var renderFilterLists = function(first) {
// Compute a hash of the settings so that we can keep track of changes
// affecting the loading of filter lists.
if ( first ) {
uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true);
uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true);
uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true);
uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true);
if ( !soft ) {
filteringSettingsHash = hashFromCurrentFromSettings();
}
renderWidgets();
@ -265,16 +269,27 @@ var renderFilterLists = function(first) {
var renderWidgets = function() {
uDom('#buttonApply').toggleClass('disabled', filteringSettingsHash === hashFromCurrentFromSettings());
uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null);
uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete > input[type="checkbox"]:checked') === null);
uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete:not(.updating) > input[type="checkbox"]:checked') === null);
};
/******************************************************************************/
var updateAssetStatus = function(details) {
var li = uDom('#lists .listEntry[data-listkey="' + details.key + '"]');
li.toggleClass('obsolete', !details.cached);
li.toggleClass('cached', details.cached);
li.removeClass('updating');
var li = document.querySelector('#lists .listEntry[data-listkey="' + details.key + '"]');
if ( li === null ) { return; }
li.classList.toggle('failed', !!details.failed);
li.classList.toggle('obsolete', !details.cached);
li.classList.toggle('cached', !!details.cached);
li.classList.remove('updating');
if ( details.cached ) {
li.querySelector('.status.cache').setAttribute(
'title',
lastUpdateTemplateString.replace(
'{{ago}}',
vAPI.i18n.renderElapsedTimeToString(Date.now())
)
);
}
renderWidgets();
};
@ -291,7 +306,7 @@ var hashFromCurrentFromSettings = function() {
document.getElementById('ignoreGenericCosmeticFilters').checked
];
var listHash = [],
listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]'),
listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
liEntry,
i = listEntries.length;
while ( i-- ) {
@ -300,7 +315,9 @@ var hashFromCurrentFromSettings = function() {
listHash.push(liEntry.getAttribute('data-listkey'));
}
}
return hash.concat(listHash.sort()).join();
hash.push(listHash.sort().join());
hash.push(document.getElementById('externalLists').value.trim());
return hash.join();
};
/******************************************************************************/
@ -311,6 +328,18 @@ var onFilteringSettingsChanged = function() {
/******************************************************************************/
var onRemoveExternalList = function(ev) {
var liEntry = uDom(this).ancestors('[data-listkey]'),
listKey = liEntry.attr('data-listkey');
if ( listKey ) {
liEntry.toggleClass('toRemove');
renderWidgets();
}
ev.preventDefault();
};
/******************************************************************************/
var onPurgeClicked = function() {
var button = uDom(this),
liEntry = button.ancestors('[data-listkey]'),
@ -322,13 +351,9 @@ var onPurgeClicked = function() {
// If the cached version is purged, the installed version must be assumed
// to be obsolete.
// https://github.com/gorhill/uBlock/issues/1733
// An external filter list must not be marked as obsolete, they will always
// be fetched anyways if there is no cached copy.
var entry = listDetails.current && listDetails.current[listKey];
if ( entry && entry.off !== true ) {
liEntry.addClass('obsolete');
uDom('#buttonUpdate').removeClass('disabled');
}
// An external filter list must not be marked as obsolete, they will
// always be fetched anyways if there is no cached copy.
liEntry.addClass('obsolete');
liEntry.removeClass('cached');
if ( liEntry.descendants('input').first().prop('checked') ) {
@ -351,24 +376,41 @@ var selectFilterLists = function(callback) {
value: document.getElementById('ignoreGenericCosmeticFilters').checked
});
// Filter lists
var listKeys = [],
liEntries = document.querySelectorAll('#lists .listEntry[data-listkey]'),
// Filter lists to select
var toSelect = [],
liEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
i = liEntries.length,
liEntry;
while ( i-- ) {
liEntry = liEntries[i];
if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
listKeys.push(liEntry.getAttribute('data-listkey'));
toSelect.push(liEntry.getAttribute('data-listkey'));
}
}
// External filter lists to remove
var toRemove = [];
liEntries = document.querySelectorAll('#lists .listEntry.toRemove[data-listkey]');
i = liEntries.length;
while ( i-- ) {
toRemove.push(liEntries[i].getAttribute('data-listkey'));
}
// External filter lists to import
var externalListsElem = document.getElementById('externalLists'),
toImport = externalListsElem.value.trim();
externalListsElem.value = '';
messaging.send(
'dashboard',
{ what: 'selectFilterLists', keys: listKeys },
{
what: 'applyFilterListSelection',
toSelect: toSelect,
toImport: toImport,
toRemove: toRemove
},
callback
);
filteringSettingsHash = hashFromCurrentFromSettings();
};
@ -387,8 +429,11 @@ var buttonApplyHandler = function() {
var buttonUpdateHandler = function() {
var onSelectionDone = function() {
uDom('#lists .listEntry.obsolete').addClass('updating');
uDom('#lists .listEntry.obsolete > input[type="checkbox"]:checked')
.ancestors('.listEntry[data-listkey]')
.addClass('updating');
messaging.send('dashboard', { what: 'forceUpdateAssets' });
renderWidgets();
};
selectFilterLists(onSelectionDone);
renderWidgets();
@ -404,7 +449,7 @@ var buttonPurgeAllHandler = function(ev) {
what: 'purgeAllCaches',
hard: ev.ctrlKey && ev.shiftKey
},
renderFilterLists
function() { renderFilterLists(true); }
);
};
@ -423,39 +468,13 @@ var autoUpdateCheckboxChanged = function() {
/******************************************************************************/
var renderExternalLists = function() {
var onReceived = function(details) {
uDom('#externalLists').val(details);
externalLists = details;
};
messaging.send(
'dashboard',
{ what: 'userSettings', name: 'externalLists' },
onReceived
);
};
/******************************************************************************/
var externalListsChangeHandler = function() {
uDom.nodeFromId('externalListsApply').disabled =
uDom.nodeFromId('externalLists').value.trim() === externalLists.trim();
};
/******************************************************************************/
var externalListsApplyHandler = function() {
externalLists = uDom.nodeFromId('externalLists').value;
messaging.send(
'dashboard',
{
what: 'userSettings',
name: 'externalLists',
value: externalLists
}
);
renderFilterLists();
uDom('#externalListsApply').prop('disabled', true);
var toggleUnusedLists = function() {
document.body.classList.toggle('hideUnused');
var hide = document.body.classList.contains('hideUnused');
uDom('#lists li.listEntry > input[type="checkbox"]:not(:checked)')
.ancestors('li.listEntry[data-listkey]')
.css('display', hide ? 'none' : '');
vAPI.localStorage.setItem('hideUnusedFilterLists', hide ? '1' : '0');
};
/******************************************************************************/
@ -478,7 +497,7 @@ var toCloudData = function() {
parseCosmeticFilters: uDom.nodeFromId('parseCosmeticFilters').checked,
ignoreGenericCosmeticFilters: uDom.nodeFromId('ignoreGenericCosmeticFilters').checked,
selectedLists: [],
externalLists: externalLists
externalLists: listDetails.externalLists
};
var liEntries = uDom('#lists .listEntry'), liEntry;
@ -529,7 +548,6 @@ var fromCloudData = function(data, append) {
elem.value += data.externalLists || '';
renderWidgets();
externalListsChangeHandler();
};
self.cloud.onPush = toCloudData;
@ -537,20 +555,25 @@ self.cloud.onPull = fromCloudData;
/******************************************************************************/
document.body.classList.toggle(
'hideUnused',
vAPI.localStorage.getItem('hideUnusedFilterLists') === '1'
);
uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
uDom('#parseCosmeticFilters').on('change', onFilteringSettingsChanged);
uDom('#ignoreGenericCosmeticFilters').on('change', onFilteringSettingsChanged);
uDom('#buttonApply').on('click', buttonApplyHandler);
uDom('#buttonUpdate').on('click', buttonUpdateHandler);
uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler);
uDom('#lists').on('change', '.listEntry > input', onFilteringSettingsChanged);
uDom('#lists').on('click', 'span.purge', onPurgeClicked);
uDom('#externalLists').on('input', externalListsChangeHandler);
uDom('#externalListsApply').on('click', externalListsApplyHandler);
uDom('#listsOfBlockedHostsPrompt').on('click', toggleUnusedLists);
uDom('#lists').on('click', '.groupEntry > span', groupEntryClickHandler);
uDom('#lists').on('change', '.listEntry > input', onFilteringSettingsChanged);
uDom('#lists').on('click', '.listEntry > a.remove', onRemoveExternalList);
uDom('#lists').on('click', 'span.cache', onPurgeClicked);
uDom('#externalLists').on('input', onFilteringSettingsChanged);
renderFilterLists(true);
renderExternalLists();
renderFilterLists();
/******************************************************************************/

View File

@ -96,7 +96,7 @@ var getTextFileFromURL = function(url, onLoad, onError) {
var onErrorReceived = function() {
this.onload = this.onerror = this.ontimeout = null;
µBlock.logger.writeOne('', 'error', errorCantConnectTo.replace('{{msg}}', ''));
µBlock.logger.writeOne('', 'error', errorCantConnectTo.replace('{{msg}}', url));
onError.call(this);
};
@ -805,7 +805,11 @@ var getRemote = function(assetKey, callback) {
};
var onRemoteContentError = function() {
registerAssetSource(assetKey, { error: { time: Date.now(), error: this.statusText } });
var text = this.statusText;
if ( this.status === 0 ) {
text = 'network error';
}
registerAssetSource(assetKey, { error: { time: Date.now(), error: text } });
tryLoading();
};
@ -948,6 +952,8 @@ var updateNext = function() {
if ( details.assetKey === 'assets.json' ) {
updateAssetSourceRegistry(details.content);
}
} else {
fireNotification('asset-update-failed', { assetKey: details.assetKey });
}
if ( findOne() !== undefined ) {
vAPI.setTimeout(updateNext, updaterAssetDelay);

View File

@ -127,6 +127,7 @@ return {
userFiltersPath: 'user-filters',
pslAssetKey: 'public_suffix_list.dat',
selectedFilterLists: [],
availableFilterLists: {},
selfieAfter: 23 * oneMinute,

View File

@ -94,10 +94,8 @@ var onMessage = function(request, sender, callback) {
var response;
switch ( request.what ) {
case 'mouseClick':
µb.mouseX = request.x;
µb.mouseY = request.y;
µb.mouseURL = request.url;
case 'applyFilterListSelection':
response = µb.applyFilterListSelection(request);
break;
case 'compileCosmeticFilterSelector':
@ -147,6 +145,12 @@ var onMessage = function(request, sender, callback) {
µb.openNewTab(request.details);
break;
case 'mouseClick':
µb.mouseX = request.x;
µb.mouseY = request.y;
µb.mouseURL = request.url;
break;
case 'reloadTab':
if ( vAPI.isBehindTheSceneTabId(request.tabId) === false ) {
vAPI.tabs.reload(request.tabId);
@ -160,10 +164,6 @@ var onMessage = function(request, sender, callback) {
µb.scriptlets.report(tabId, request.scriptlet, request.response);
break;
case 'selectFilterLists':
µb.saveSelectedFilterLists(request.keys, request.append);
break;
case 'setWhitelist':
µb.netWhitelist = µb.whitelistFromString(request.whitelist);
µb.saveWhitelist();
@ -873,11 +873,12 @@ var getLists = function(callback) {
autoUpdate: µb.userSettings.autoUpdate,
available: null,
cache: null,
parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters,
cosmeticFilterCount: µb.cosmeticFilteringEngine.getFilterCount(),
current: µb.availableFilterLists,
externalLists: µb.userSettings.externalLists,
ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters,
netFilterCount: µb.staticNetFilteringEngine.getFilterCount(),
parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters,
userFiltersPath: µb.userFiltersPath,
aliases: µb.assets.listKeyAliases
};
@ -1287,8 +1288,7 @@ var onMessage = function(request, sender, callback) {
case 'subscriberData':
response = {
confirmStr: vAPI.i18n('subscriberConfirm'),
externalLists: µBlock.userSettings.externalLists
confirmStr: vAPI.i18n('subscriberConfirm')
};
break;

View File

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
/* global vAPI, HTMLDocument */
/* global HTMLDocument */
'use strict';
@ -79,9 +79,7 @@ var onAbpLinkClicked = function(ev) {
var matches = /^abp:\/*subscribe\/*\?location=([^&]+).*title=([^&]+)/.exec(href);
if ( matches === null ) {
matches = /^https?:\/\/.*?[&?]location=([^&]+).*?&title=([^&]+)/.exec(href);
if ( matches === null ) {
return;
}
if ( matches === null ) { return; }
}
var location = decodeURIComponent(matches[1]);
@ -95,44 +93,18 @@ var onAbpLinkClicked = function(ev) {
messaging.send('scriptlets', { what: 'reloadAllFilters' });
};
var onExternalListsSaved = function() {
messaging.send(
'scriptlets',
{
what: 'selectFilterLists',
keys: [ location ],
append: true
},
onListsSelectionDone
);
};
var onSubscriberDataReady = function(details) {
var confirmStr = details.confirmStr
.replace('{{url}}', location)
.replace('{{title}}', title);
if ( !window.confirm(confirmStr) ) {
return;
}
// List already subscribed to?
// https://github.com/chrisaljoudi/uBlock/issues/1033
// Split on line separators, not whitespaces.
var text = details.externalLists.trim();
var lines = text !== '' ? text.split(/\s*[\n\r]+\s*/) : [];
if ( lines.indexOf(location) !== -1 ) {
return;
}
lines.push(location, '');
if ( !window.confirm(confirmStr) ) { return; }
messaging.send(
'scriptlets',
{
what: 'userSettings',
name: 'externalLists',
value: lines.join('\n')
what: 'applyFilterListSelection',
toImport: location
},
onExternalListsSaved
onListsSelectionDone
);
};

View File

@ -186,28 +186,33 @@
// Uncomment when all have moved to v1.11 and beyond.
//vAPI.storage.remove('remoteBlacklists');
}
µb.selectedFilterLists = listKeys.slice();
callback(listKeys);
});
};
µBlock.saveSelectedFilterLists = function(listKeys, append) {
µBlock.saveSelectedFilterLists = function(newKeys, append) {
var µb = this;
var save = function(keys) {
var uniqueKeys = µb.setToArray(new Set(keys));
this.loadSelectedFilterLists(function(oldKeys) {
oldKeys = oldKeys || [];
if ( append ) {
newKeys = newKeys.concat(oldKeys);
}
var newSet = new Set(newKeys);
// Purge unused filter lists from cache.
for ( var i = 0, n = oldKeys.length; i < n; i++ ) {
if ( newSet.has(oldKeys[i]) === false ) {
µb.removeFilterList(oldKeys[i]);
}
}
newKeys = µb.setToArray(newSet);
var bin = {
selectedFilterLists: uniqueKeys,
remoteBlacklists: µb.oldDataFromNewListKeys(uniqueKeys)
selectedFilterLists: newKeys,
remoteBlacklists: µb.oldDataFromNewListKeys(newKeys)
};
µb.selectedFilterLists = newKeys;
vAPI.storage.set(bin);
};
if ( append ) {
this.loadSelectedFilterLists(function(keys) {
listKeys = listKeys.concat(keys || []);
save(listKeys);
});
} else {
save(listKeys);
}
});
};
// TODO(seamless migration):
@ -262,6 +267,113 @@
/******************************************************************************/
µBlock.applyFilterListSelection = function(details, callback) {
var µb = this,
selectedListKeySet = new Set(this.selectedFilterLists),
externalLists = this.userSettings.externalLists,
i, n, assetKey;
// Filter lists to select
if ( Array.isArray(details.toSelect) ) {
if ( details.merge ) {
for ( i = 0, n = details.toSelect.length; i < n; i++ ) {
selectedListKeySet.add(details.toSelect[i]);
}
} else {
selectedListKeySet = new Set(details.toSelect);
}
}
// Imported filter lists to remove
if ( Array.isArray(details.toRemove) ) {
var removeURLFromHaystack = function(haystack, needle) {
return haystack.replace(
new RegExp(
'(^|\\n)' +
needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
'(\\n|$)', 'g'),
'\n'
).trim();
};
for ( i = 0, n = details.toRemove.length; i < n; i++ ) {
assetKey = details.toRemove[i];
selectedListKeySet.delete(assetKey);
externalLists = removeURLFromHaystack(externalLists, assetKey);
this.removeFilterList(assetKey);
}
}
// Filter lists to import
if ( typeof details.toImport === 'string' ) {
// https://github.com/gorhill/uBlock/issues/1181
// Try mapping the URL of an imported filter list to the assetKey of an
// existing stock list.
var assetKeyFromURL = function(url) {
var needle = url.replace(/^https?:/, '');
var assets = µb.availableFilterLists, asset;
for ( var assetKey in assets ) {
asset = assets[assetKey];
if ( asset.content !== 'filters' ) { continue; }
if ( typeof asset.contentURL === 'string' ) {
if ( asset.contentURL.endsWith(needle) ) { return assetKey; }
continue;
}
if ( Array.isArray(asset.contentURL) === false ) { continue; }
for ( i = 0, n = asset.contentURL.length; i < n; i++ ) {
if ( asset.contentURL[i].endsWith(needle) ) {
return assetKey;
}
}
}
return url;
};
var importedSet = new Set(this.listKeysFromCustomFilterLists(externalLists)),
toImportSet = new Set(this.listKeysFromCustomFilterLists(details.toImport)),
iter = toImportSet.values();
for (;;) {
var entry = iter.next();
if ( entry.done ) { break; }
if ( importedSet.has(entry.value) ) { continue; }
assetKey = assetKeyFromURL(entry.value);
if ( assetKey === entry.value ) {
importedSet.add(entry.value);
}
selectedListKeySet.add(assetKey);
}
externalLists = this.setToArray(importedSet).sort().join('\n');
}
var result = this.setToArray(selectedListKeySet);
if ( externalLists !== this.userSettings.externalLists ) {
this.userSettings.externalLists = externalLists;
vAPI.storage.set({ externalLists: externalLists });
}
this.saveSelectedFilterLists(result);
if ( typeof callback === 'function' ) {
callback(result);
}
};
/******************************************************************************/
µBlock.listKeysFromCustomFilterLists = function(raw) {
var out = new Set(),
reIgnore = /^[!#]/,
reValid = /^[a-z-]+:\/\/\S+/,
lineIter = new this.LineIterator(raw),
location;
while ( lineIter.eot() === false ) {
location = lineIter.next().trim();
if ( reIgnore.test(location) || !reValid.test(location) ) {
continue;
}
out.add(location);
}
return this.setToArray(out);
};
/******************************************************************************/
µBlock.saveUserFilters = function(content, callback) {
// https://github.com/gorhill/uBlock/issues/1022
// Be sure to end with an empty line.
@ -315,21 +427,6 @@
/******************************************************************************/
µBlock.listKeysFromCustomFilterLists = function(raw) {
var out = {};
var reIgnore = /^[!#]|[^0-9A-Za-z!*'();:@&=+$,\/?%#\[\]_.~-]/,
lineIter = new this.LineIterator(raw),
location;
while ( lineIter.eot() === false ) {
location = lineIter.next().trim();
if ( location === '' || reIgnore.test(location) ) { continue; }
out[location] = true;
}
return Object.keys(out);
};
/******************************************************************************/
µBlock.autoSelectRegionalFilterLists = function(lists) {
var lang = self.navigator.language.slice(0, 2),
selectedListKeys = [],
@ -351,41 +448,6 @@
/******************************************************************************/
µBlock.changeExternalFilterLists = function(before, after) {
var µb = µBlock;
var onLoaded = function(keys) {
var fullDict = new Set(keys || []),
mustSave = false,
oldKeys = µb.listKeysFromCustomFilterLists(before),
oldDict = new Set(oldKeys),
newKeys = µb.listKeysFromCustomFilterLists(after),
newDict = new Set(newKeys),
i, key;
i = oldKeys.length;
while ( i-- ) {
key = oldKeys[i];
if ( fullDict.has(key) && !newDict.has(key) ) {
fullDict.delete(key);
mustSave = true;
}
}
i = newKeys.length;
while ( i-- ) {
key = newKeys[i];
if ( !fullDict.has(key) && !oldDict.has(key) ) {
fullDict.add(key);
mustSave = true;
}
}
if ( mustSave ) {
µb.saveSelectedFilterLists(µb.setToArray(fullDict));
}
};
this.loadSelectedFilterLists(onLoaded);
};
/******************************************************************************/
µBlock.getAvailableLists = function(callback) {
var µb = this,
oldAvailableLists = {},
@ -1006,7 +1068,7 @@
if ( topic === 'before-asset-updated' ) {
if (
this.availableFilterLists.hasOwnProperty(details.assetKey) &&
this.availableFilterLists[details.assetKey].off === true
this.selectedFilterLists.indexOf(details.assetKey) === -1
) {
return false;
}
@ -1018,7 +1080,7 @@
var cached = typeof details.content === 'string' && details.content !== '';
if ( this.availableFilterLists.hasOwnProperty(details.assetKey) ) {
if ( cached ) {
if ( this.availableFilterLists[details.assetKey].off !== true ) {
if ( this.selectedFilterLists.indexOf(details.assetKey) !== -1 ) {
this.extractFilterListMetadata(
details.assetKey,
details.content
@ -1049,6 +1111,16 @@
return;
}
// Update failed.
if ( topic === 'asset-update-failed' ) {
vAPI.messaging.broadcast({
what: 'assetUpdated',
key: details.assetKey,
failed: true
});
return;
}
// Reload all filter lists if needed.
if ( topic === 'after-assets-updated' ) {
if ( details.assetKeys.length !== 0 ) {

View File

@ -317,9 +317,6 @@ var reInvalidHostname = /[^a-z0-9.\-\[\]:]/,
// Pre-change
switch ( name ) {
case 'externalLists':
this.changeExternalFilterLists(us.externalLists, value);
break;
case 'largeMediaSize':
if ( typeof value !== 'number' ) {
value = parseInt(value, 10) || 0;