1
0
Fork 0
mirror of https://github.com/gorhill/uMatrix.git synced 2024-06-02 02:14:52 +12:00

assets updater, hosts files pane, etc.

This commit is contained in:
gorhill 2015-05-01 19:27:43 -04:00
parent f4807b6b29
commit c472e23c7d
13 changed files with 574 additions and 290 deletions

View file

@ -449,6 +449,10 @@
"message": "{{used}} used out of {{total}}",
"description": ""
},
"hostsFilesLastUpdate" : {
"message":"Last update: {{ago}}",
"description":"English: Last update: {{ago}}, where 'ago' will be replaced with something like '2 days ago'"
},
"hostsFilesApplyChanges" : {
"message": "Apply changes",
"description": ""
@ -582,6 +586,30 @@
"description": ""
},
"elapsedOneMinuteAgo":{
"message":"a minute ago",
"description":"English: a minute ago"
},
"elapsedManyMinutesAgo":{
"message":"{{value}} minutes ago",
"description":"English: {{value}} minutes ago"
},
"elapsedOneHourAgo":{
"message":"an hour ago",
"description":"English: an hour ago"
},
"elapsedManyHoursAgo":{
"message":"{{value}} hours ago",
"description":"English: {{value}} hours ago"
},
"elapsedOneDayAgo":{
"message":"a day ago",
"description":"English: a day ago"
},
"elapsedManyDaysAgo":{
"message":"{{value}} days ago",
"description":"English: {{value}} days ago"
},
"errorCantConnectTo":{
"message":"Network error: Unable to connect to {{url}}",

View file

@ -12,6 +12,7 @@
</head>
<body>
<div id="content"></div>
<script src="js/vapi-common.js"></script>
<script src="js/vapi-client.js"></script>
<script src="js/udom.js"></script>
<script src="js/asset-viewer.js"></script>

View file

@ -19,7 +19,6 @@
<script src="js/matrix.js"></script>
<script src="js/utils.js"></script>
<script src="js/assets.js"></script>
<script src="js/updater.js"></script>
<script src="js/httpsb.js"></script>
<script src="js/reqstats.js"></script>
<script src="js/cookies.js"></script>

View file

@ -10,19 +10,25 @@ ul#options li {
}
ul#lists {
margin: 0.5em 0 0 0;
padding-__MSG_@@bidi_end_edge__: 0em;
padding-__MSG_@@bidi_start_edge__: 1em;
padding: 0;
}
li.listDetails {
li.listEntry {
font-size: 14px;
margin: 0 auto 0 auto;
margin-__MSG_@@bidi_start_edge__: 1em;
margin-__MSG_@@bidi_end_edge__: 0em;
padding: 0.2em 0;
}
li.listDetails > * {
body[dir="ltr"] li.listEntry {
margin-left: 1em;
margin-right: 0em;
}
body[dir="rtl"] li.listEntry {
margin-left: 0em;
margin-right: 1em;
}
li.listEntry > * {
unicode-bidi: embed;
}
li.listDetails > a:nth-of-type(2) {
li.listEntry > a:nth-of-type(2) {
font-size: 13px;
opacity: 0.5;
}
@ -58,25 +64,36 @@ button.custom.reloadAll:not(.disabled) {
background-image: linear-gradient(#ffdca8, #ffcc7f);
}
#buttonApply {
position: fixed;
display: initial;
padding: 1em 1em;
position: fixed;
top: 1em;
__MSG_@@bidi_end_edge__: 1em;
}
body[dir="ltr"] #buttonApply {
right: 1em;
}
body[dir="rtl"] #buttonApply {
left: 1em;
}
#buttonApply.disabled {
display: none;
}
span.status {
margin: 0;
border: 1px solid transparent;
padding: 1px 2px;
color: #444;
display: inline-block;
font-size: 11px;
opacity: 0.7;
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%);
}
span.purge {
border-color: #ddd;
color: #444;
background-color: #eee;
cursor: pointer;
}
@ -85,31 +102,82 @@ span.purge:hover {
}
span.obsolete {
border-color: hsl(36, 100%, 73%);
color: #222;
background-color: hsl(36, 100%, 75%);
}
#externalListsDiv {
margin: 2em auto 0 auto;
margin-__MSG_@@bidi_start_edge__: 2em;
}
body[dir="ltr"] #externalListsDiv {
margin-left: 1em;
}
body[dir="rtl"] #externalListsDiv {
margin-right: 1em;
}
#externalHostsFiles {
box-sizing: border-box;
font-size: smaller;
width: 48em;
width: 100%;
height: 12em;
white-space: nowrap;
}
body #busyOverlay {
position: fixed;
top: 0;
right: 0;
background-color: transparent;
bottom: 0;
left: 0;
background-color: white;
opacity: 0.5;
cursor: wait;
display: none;
left: 0;
position: fixed;
right: 0;
top: 0;
z-index: 1000;
}
body.busy #busyOverlay {
display: block;
}
#busyOverlay > div:nth-of-type(1) {
background-color: white;
bottom: 0;
left: 0;
opacity: 0.75;
position: absolute;
right: 0;
top: 0;
}
#busyOverlay > div:nth-of-type(2) {
background-color: #eee;
border: 1px solid transparent;
border-color: #80b3ff #80b3ff hsl(216, 100%, 75%);
border-radius: 3px;
box-sizing: border-box;
height: 3em;
left: 10%;
position: absolute;
bottom: 75%;
width: 80%;
}
#busyOverlay > div:nth-of-type(2) > div:nth-of-type(1) {
background-color: hsl(216, 100%, 75%);
background-image: linear-gradient(#a8cbff, #80b3ff);
background-repeat: repeat-x;
border: 0;
box-sizing: border-box;
color: #222;
height: 100%;
left: 0;
padding: 0;
position: absolute;
width: 25%;
}
#busyOverlay > div:nth-of-type(2) > div:nth-of-type(2) {
background-color: transparent;
border: 0;
box-sizing: border-box;
height: 100%;
left: 0;
line-height: 3em;
overflow: hidden;
position: absolute;
text-align: center;
top: 0;
width: 100%;
}

View file

@ -27,7 +27,29 @@
<p style="margin: 0.25em 0 0 0"><button id="externalListsParse" disabled="true" data-i18n="hostsFilesExternalListsParse"></button></p>
</div>
<div id="busyOverlay"></div>
<div id="busyOverlay">
<div></div>
<!-- progress bar widget -->
<div><div></div><div></div></div>
</div>
<div id="templates" style="display: none;">
<ul>
<li class="groupEntry"><span class="geName"></span> <span class="geCount dim"></span>
<ul></ul>
</li>
<li class="listEntry">
<input type="checkbox">
<a type="text/plain" target="_blank" href=""></a>
<a href="" style="display: none;" target="_blank"></a>: <!--
--><span class="dim"></span><!--
--><span class="status unsecure" style="display: none;">http</span><!--
--><span class="status new" style="display: none;" data-i18n="hostsFilesExternalListNew"></span><!--
--><span class="status obsolete" style="display: none;" data-i18n="hostsFilesExternalListObsolete"></span><!--
--><span class="status purge" style="display: none;" data-i18n="hostsFilesExternalListPurge"></span>
</li>
</ul>
</div>
<script src="js/vapi-common.js"></script>
<script src="js/vapi-client.js"></script>

View file

@ -1121,7 +1121,7 @@ exports.purgeAll = function(callback) {
/******************************************************************************/
exports.onAssetCacheRemoved = {
addEventListener: function(callback) {
addListener: function(callback) {
cachedAssetsManager.onRemovedListener = callback || null;
}
};
@ -1436,7 +1436,7 @@ exports.force = function() {
/******************************************************************************/
exports.onStart = {
addEventListener: function(callback) {
addListener: function(callback) {
onStartListener = callback || null;
if ( typeof onStartListener === 'function' ) {
updateCycleTime = Date.now() + updateCycleFirstPeriod;
@ -1447,7 +1447,7 @@ exports.onStart = {
/******************************************************************************/
exports.onAssetUpdated = {
addEventListener: function(callback) {
addListener: function(callback) {
onAssetUpdatedListener = callback || null;
}
};
@ -1455,7 +1455,7 @@ exports.onAssetUpdated = {
/******************************************************************************/
exports.onCompleted = {
addEventListener: function(callback) {
addListener: function(callback) {
onCompletedListener = callback || null;
}
};

View file

@ -86,10 +86,6 @@ return {
// permanent hosts files
permanentHostsFiles: {
// µMatrix
'assets/umatrix/blacklist.txt': {
title: 'µMatrix hosts file'
}
},
// list of live hosts files

View file

@ -1,7 +1,7 @@
/*******************************************************************************
µMatrix - a Chromium browser extension to black/white list requests.
Copyright (C) 2014 Raymond Hill
Copyright (C) 2014-2015 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
@ -29,14 +29,29 @@
/******************************************************************************/
var listDetails = {};
var externalHostsFiles = '';
var cacheWasPurged = false;
var needUpdate = false;
var hasCachedContent = false;
/******************************************************************************/
var onMessage = function(msg) {
switch ( msg.what ) {
case 'loadHostsFilesCompleted':
renderBlacklists();
break;
case 'loadHostsFilesCompleted':
renderHostsFiles();
break;
default:
break;
case 'forceUpdateAssetsProgress':
renderBusyOverlay(true, msg.progress);
if ( msg.done ) {
messager.send({ what: 'reloadHostsFiles' });
}
break;
default:
break;
}
};
@ -44,21 +59,19 @@ var messager = vAPI.messaging.channel('hosts-files.js', onMessage);
/******************************************************************************/
var listDetails = {};
var externalHostsFiles = '';
var cacheWasPurged = false;
var needUpdate = false;
var hasCachedContent = false;
var re3rdPartyExternalAsset = /^https?:\/\/[a-z0-9]+/;
var re3rdPartyRepoAsset = /^assets\/thirdparties\/([^\/]+)/;
var renderNumber = function(value) {
return value.toLocaleString();
};
/******************************************************************************/
// TODO: get rid of background page dependencies
var renderBlacklists = function() {
uDom('body').toggleClass('busy', true);
var renderHostsFiles = function() {
var listEntryTemplate = uDom('#templates .listEntry');
var listStatsTemplate = vAPI.i18n('hostsFilesPerFileStats');
var lastUpdateString = vAPI.i18n('hostsFilesLastUpdate');
var renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString;
// Assemble a pretty blacklist name if possible
var listNameFromListKey = function(listKey) {
@ -70,89 +83,67 @@ var renderBlacklists = function() {
return listTitle;
};
// Assemble a pretty blacklist name if possible
var htmlFromHomeURL = function(blacklistHref) {
if ( blacklistHref.indexOf('assets/thirdparties/') !== 0 ) {
return '';
}
var matches = re3rdPartyRepoAsset.exec(blacklistHref);
if ( matches === null || matches.length !== 2 ) {
return '';
}
var hostname = matches[1];
var domain = hostname;
if ( domain === '' ) {
return '';
}
var html = [
' <a href="http://',
hostname,
'" target="_blank">(',
domain,
')</a>'
];
return html.join('');
};
var liFromListEntry = function(listKey) {
var elem, text;
var entry = listDetails.available[listKey];
var li = listEntryTemplate.clone();
var purgeButtontext = vAPI.i18n('hostsFilesExternalListPurge');
var updateButtontext = vAPI.i18n('hostsFilesExternalListNew');
var obsoleteButtontext = vAPI.i18n('hostsFilesExternalListObsolete');
var liTemplate = [
'<li class="listDetails">',
'<input type="checkbox" {{checked}}>',
' ',
'<a href="{{URL}}" type="text/plain">',
'{{name}}',
'\u200E</a>',
'{{homeURL}}',
': ',
'<span class="dim">',
vAPI.i18n('hostsFilesPerFileStats'),
'</span>'
].join('');
var htmlFromLeaf = function(listKey) {
var html = [];
var hostsEntry = listDetails.available[listKey];
var li = liTemplate
.replace('{{checked}}', hostsEntry.off ? '' : 'checked')
.replace('{{URL}}', encodeURI(listKey))
.replace('{{name}}', listNameFromListKey(listKey))
.replace('{{homeURL}}', htmlFromHomeURL(listKey))
.replace('{{used}}', !hostsEntry.off && !isNaN(+hostsEntry.entryUsedCount) ? hostsEntry.entryUsedCount.toLocaleString() : '0')
.replace('{{total}}', !isNaN(+hostsEntry.entryCount) ? hostsEntry.entryCount.toLocaleString() : '?');
html.push(li);
// https://github.com/gorhill/uBlock/issues/104
var asset = listDetails.cache[listKey];
if ( asset === undefined ) {
return html.join('\n');
if ( entry.off !== true ) {
li.descendants('input').attr('checked', '');
}
// Update status
if ( hostsEntry.off !== true ) {
var obsolete = asset.repoObsolete ||
asset.cacheObsolete ||
asset.cached !== true && re3rdPartyExternalAsset.test(listKey);
if ( obsolete ) {
html.push(
'&ensp;',
'<span class="status obsolete">',
asset.repoObsolete ? updateButtontext : obsoleteButtontext,
'</span>'
);
elem = li.descendants('a:nth-of-type(1)');
elem.attr('href', encodeURI(listKey));
elem.text(listNameFromListKey(listKey) + '\u200E');
elem = li.descendants('a:nth-of-type(2)');
if ( entry.homeDomain ) {
elem.attr('href', 'http://' + encodeURI(entry.homeHostname));
elem.text('(' + entry.homeDomain + ')');
elem.css('display', '');
}
elem = li.descendants('span:nth-of-type(1)');
text = listStatsTemplate
.replace('{{used}}', renderNumber(!entry.off && !isNaN(+entry.entryUsedCount) ? entry.entryUsedCount : 0))
.replace('{{total}}', !isNaN(+entry.entryCount) ? renderNumber(entry.entryCount) : '?');
elem.text(text);
// https://github.com/gorhill/uBlock/issues/78
// Badge for non-secure connection
var remoteURL = listKey;
if ( remoteURL.lastIndexOf('http:', 0) !== 0 ) {
remoteURL = entry.homeURL || '';
}
if ( remoteURL.lastIndexOf('http:', 0) === 0 ) {
li.descendants('span.status.unsecure').css('display', '');
}
// https://github.com/chrisaljoudi/uBlock/issues/104
var asset = listDetails.cache[listKey] || {};
// Badge for update status
if ( entry.off !== true ) {
if ( asset.repoObsolete ) {
li.descendants('span.status.new').css('display', '');
needUpdate = true;
} else if ( asset.cacheObsolete ) {
li.descendants('span.status.obsolete').css('display', '');
needUpdate = true;
} else if ( entry.external && !asset.cached ) {
li.descendants('span.status.obsolete').css('display', '');
needUpdate = true;
}
}
// In cache
if ( asset.cached ) {
html.push(
'&ensp;',
'<span class="status purge">',
purgeButtontext,
'</span>'
);
elem = li.descendants('span.status.purge');
elem.css('display', '');
elem.attr('title', lastUpdateString.replace('{{ago}}', renderElapsedTimeToString(asset.lastModified)));
hasCachedContent = true;
}
return html.join('\n');
return li;
};
var onListsReceived = function(details) {
@ -161,34 +152,37 @@ var renderBlacklists = function() {
needUpdate = false;
hasCachedContent = false;
// Visually split the filter lists in two groups: built-in and external
var htmlBuiltin = [];
var htmlExternal = [];
var hostsPaths = Object.keys(details.available);
var hostsPath, hostsEntry;
for ( var i = 0; i < hostsPaths.length; i++ ) {
hostsPath = hostsPaths[i];
hostsEntry = details.available[hostsPath];
if ( hostsEntry.external ) {
htmlExternal.push(htmlFromLeaf(hostsPath, hostsEntry));
} else {
htmlBuiltin.push(htmlFromLeaf(hostsPath, hostsEntry));
var availableLists = details.available;
var listKeys = Object.keys(details.available);
listKeys.sort(function(a, b) {
var ta = availableLists[a].title || '';
var tb = availableLists[b].title || '';
if ( ta !== '' && tb !== '' ) {
return ta.localeCompare(tb);
}
if ( ta === '' && tb === '' ) {
return a.localeCompare(b);
}
if ( tb === '' ) {
return -1;
}
return 1;
});
var ulList = uDom('#lists').empty();
for ( var i = 0; i < listKeys.length; i++ ) {
ulList.append(liFromListEntry(listKeys[i]));
}
if ( htmlExternal.length !== 0 ) {
htmlBuiltin.push('<li>&nbsp;');
}
var html = htmlBuiltin.concat(htmlExternal);
uDom('#listsOfBlockedHostsPrompt').text(
vAPI.i18n('hostsFilesStats')
.replace('{{blockedHostnameCount}}', details.blockedHostnameCount.toLocaleString())
vAPI.i18n('hostsFilesStats').replace(
'{{blockedHostnameCount}}',
renderNumber(details.blockedHostnameCount)
)
);
uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
uDom('#lists').html(html.join(''));
uDom('a').attr('target', '_blank');
updateWidgets();
renderWidgets();
renderBusyOverlay(details.manualUpdate, details.manualUpdateProgress);
};
messager.send({ what: 'getLists' }, onListsReceived);
@ -196,6 +190,37 @@ var renderBlacklists = function() {
/******************************************************************************/
// Progress must be normalized to [0, 1], or can be undefined.
var renderBusyOverlay = function(state, progress) {
progress = progress || {};
var showProgress = typeof progress.value === 'number';
if ( showProgress ) {
uDom('#busyOverlay > div:nth-of-type(2) > div:first-child').css(
'width',
(progress.value * 100).toFixed(1) + '%'
);
var text = progress.text || '';
if ( text !== '' ) {
uDom('#busyOverlay > div:nth-of-type(2) > div:last-child').text(text);
}
}
uDom('#busyOverlay > div:nth-of-type(2)').css('display', showProgress ? '' : 'none');
uDom('body').toggleClass('busy', !!state);
};
/******************************************************************************/
// This is to give a visual hint that the selection of blacklists has changed.
var renderWidgets = function() {
uDom('#buttonApply').toggleClass('disabled', !listsSelectionChanged());
uDom('#buttonUpdate').toggleClass('disabled', !listsContentChanged());
uDom('#buttonPurgeAll').toggleClass('disabled', !hasCachedContent);
};
/******************************************************************************/
// Return whether selection of lists changed.
var listsSelectionChanged = function() {
@ -292,55 +317,70 @@ var onPurgeClicked = function() {
/******************************************************************************/
var reloadAll = function(update) {
// Loading may take a while when resources are fetched from remote
// servers. We do not want the user to force reload while we are reloading.
uDom('body').toggleClass('busy', true);
// Reload blacklists
var selectHostsFiles = function(callback) {
var switches = [];
var lis = uDom('#lists .listDetails');
var lis = uDom('#lists .listEntry'), li;
var i = lis.length;
var path;
while ( i-- ) {
path = lis
.subset(i, 1)
.descendants('a')
.attr('href');
li = lis.at(i);
switches.push({
location: path,
off: lis.subset(i, 1).descendants('input').prop('checked') === false
location: li.descendants('a').attr('href'),
off: li.descendants('input').prop('checked') === false
});
}
messager.send({
what: 'reloadHostsFiles',
switches: switches,
update: update
});
cacheWasPurged = false;
what: 'selectHostsFiles',
switches: switches
}, callback);
};
/******************************************************************************/
var buttonApplyHandler = function() {
reloadAll(false);
uDom('#buttonApply').toggleClass('enabled', false);
uDom('#buttonApply').removeClass('enabled');
renderBusyOverlay(true);
var onSelectionDone = function() {
messager.send({ what: 'reloadHostsFiles' });
};
selectHostsFiles(onSelectionDone);
cacheWasPurged = false;
};
/******************************************************************************/
var buttonUpdateHandler = function() {
uDom('#buttonUpdate').removeClass('enabled');
if ( needUpdate ) {
reloadAll(true);
renderBusyOverlay(true);
var onSelectionDone = function() {
messager.send({ what: 'forceUpdateAssets' });
};
selectHostsFiles(onSelectionDone);
cacheWasPurged = false;
}
};
/******************************************************************************/
var buttonPurgeAllHandler = function() {
uDom('#buttonPurgeAll').removeClass('enabled');
renderBusyOverlay(true);
var onCompleted = function() {
renderBlacklists();
cacheWasPurged = true;
renderHostsFiles();
};
messager.send({ what: 'purgeAllCaches' }, onCompleted);
};
@ -382,7 +422,7 @@ var externalListsApplyHandler = function() {
name: 'externalHostsFiles',
value: externalHostsFiles
});
renderBlacklists();
renderHostsFiles();
uDom('#externalListsParse').prop('disabled', true);
};
@ -393,13 +433,13 @@ uDom.onLoad(function() {
uDom('#buttonApply').on('click', buttonApplyHandler);
uDom('#buttonUpdate').on('click', buttonUpdateHandler);
uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler);
uDom('#lists').on('change', '.listDetails > input', onListCheckboxChanged);
uDom('#lists').on('click', '.listDetails > a:nth-of-type(1)', onListLinkClicked);
uDom('#lists').on('change', '.listEntry > input', onListCheckboxChanged);
uDom('#lists').on('click', '.listEntry > a:nth-of-type(1)', onListLinkClicked);
uDom('#lists').on('click', 'span.purge', onPurgeClicked);
uDom('#externalHostsFiles').on('input', externalListsChangeHandler);
uDom('#externalListsParse').on('click', externalListsApplyHandler);
renderBlacklists();
renderHostsFiles();
renderExternalLists();
});

View file

@ -27,6 +27,7 @@
var µm = µMatrix;
µm.pMatrix = new µm.Matrix();
µm.pMatrix.setSwitch('matrix-off', 'localhost', 1);
µm.pMatrix.setSwitch('matrix-off', 'about-scheme', 1);
µm.pMatrix.setSwitch('matrix-off', 'chrome-extension-scheme', 1);
µm.pMatrix.setSwitch('matrix-off', 'chrome-scheme', 1);
µm.pMatrix.setSwitch('matrix-off', µm.behindTheSceneScope, 1);

View file

@ -19,29 +19,65 @@
Home: https://github.com/gorhill/uMatrix
*/
// Helper to deal with the i18n'ing of HTML files.
// jQuery must be present at this point.
/******************************************************************************/
window.addEventListener('load', function() {
'use strict';
// This file should always be included at the end of the `body` tag, so as
// to ensure all i18n targets are already loaded.
var nodeList = document.querySelectorAll('[data-i18n]');
var i = nodeList.length;
var node;
while ( i-- ) {
node = nodeList[i];
vAPI.insertHTML(node, vAPI.i18n(node.getAttribute('data-i18n')));
(function() {
'use strict';
/******************************************************************************/
var nodeList = document.querySelectorAll('[data-i18n]');
var i = nodeList.length;
var node;
while ( i-- ) {
node = nodeList[i];
vAPI.insertHTML(node, vAPI.i18n(node.getAttribute('data-i18n')));
}
// copy text of <h1> if any to document title
node = document.querySelector('h1');
if ( node !== null ) {
document.title = node.textContent;
}
// Tool tips
nodeList = document.querySelectorAll('[data-i18n-tip]');
i = nodeList.length;
while ( i-- ) {
node = nodeList[i];
node.setAttribute('data-tip', vAPI.i18n(node.getAttribute('data-i18n-tip')));
}
/******************************************************************************/
vAPI.i18n.renderElapsedTimeToString = function(tstamp) {
var value = (Date.now() - tstamp) / 60000;
if ( value < 2 ) {
return vAPI.i18n('elapsedOneMinuteAgo');
}
// copy text of <h1> if any to document title
node = document.querySelector('h1');
if ( node !== null ) {
document.title = node.textContent;
if ( value < 60 ) {
return vAPI.i18n('elapsedManyMinutesAgo').replace('{{value}}', Math.floor(value).toLocaleString());
}
// Tool tips
nodeList = document.querySelectorAll('[data-i18n-tip]');
i = nodeList.length;
while ( i-- ) {
node = nodeList[i];
node.setAttribute('data-tip', vAPI.i18n(node.getAttribute('data-i18n-tip')));
value /= 60;
if ( value < 2 ) {
return vAPI.i18n('elapsedOneHourAgo');
}
});
if ( value < 24 ) {
return vAPI.i18n('elapsedManyHoursAgo').replace('{{value}}', Math.floor(value).toLocaleString());
}
value /= 24;
if ( value < 2 ) {
return vAPI.i18n('elapsedOneDayAgo');
}
return vAPI.i18n('elapsedManyDaysAgo').replace('{{value}}', Math.floor(value).toLocaleString());
};
/******************************************************************************/
})();
/******************************************************************************/

View file

@ -55,6 +55,10 @@ function onMessage(request, sender, callback) {
µm.forceReload(request.tabId);
break;
case 'forceUpdateAssets':
µm.assetUpdater.force();
break;
case 'getUserSettings':
response = µm.userSettings;
break;
@ -68,7 +72,11 @@ function onMessage(request, sender, callback) {
break;
case 'reloadHostsFiles':
µm.reloadHostsFiles(request.switches, request.update);
µm.reloadHostsFiles();
break;
case 'selectHostsFiles':
µm.selectHostsFiles(request.switches);
break;
case 'userSettings':
@ -637,20 +645,41 @@ var µm = µMatrix;
/******************************************************************************/
var prepEntries = function(entries) {
var µmuri = µm.URI;
var entry;
for ( var k in entries ) {
if ( entries.hasOwnProperty(k) === false ) {
continue;
}
entry = entries[k];
if ( typeof entry.homeURL === 'string' ) {
entry.homeHostname = µmuri.hostnameFromURI(entry.homeURL);
entry.homeDomain = µmuri.domainFromHostname(entry.homeHostname);
}
}
};
/******************************************************************************/
var getLists = function(callback) {
var r = {
autoUpdate: µm.userSettings.autoUpdate,
available: null,
cache: null,
current: µm.liveHostsFiles,
blockedHostnameCount: µm.ubiquitousBlacklist.count,
autoUpdate: µm.userSettings.autoUpdate
blockedHostnameCount: µm.ubiquitousBlacklist.count
};
var onMetadataReady = function(entries) {
r.cache = entries;
prepEntries(r.cache);
r.manualUpdate = µm.assetUpdater.manualUpdate;
r.manualUpdateProgress = µm.assetUpdater.manualUpdateProgress;
callback(r);
};
var onAvailableHostsFilesReady = function(lists) {
r.available = lists;
prepEntries(r.available);
µm.assets.metadata(onMetadataReady);
};
µm.getAvailableHostsFiles(onAvailableHostsFilesReady);

View file

@ -70,18 +70,12 @@
(function() {
var µm = µMatrix;
var jobDone = function(details) {
if ( details.changedCount === 0 ) {
return;
}
µm.loadUpdatableAssets();
};
var jobCallback = function() {
µm.assetUpdater.update(null, jobDone);
};
µm.asyncJobs.add('autoUpdateAssets', null, jobCallback, µm.updateAssetsEvery, true);
// https://github.com/chrisaljoudi/uBlock/issues/184
// Check for updates not too far in the future.
µm.assetUpdater.onStart.addListener(µm.updateStartHandler.bind(µm));
µm.assetUpdater.onCompleted.addListener(µm.updateCompleteHandler.bind(µm));
µm.assetUpdater.onAssetUpdated.addListener(µm.assetUpdatedHandler.bind(µm));
µm.assets.onAssetCacheRemoved.addListener(µm.assetCacheRemovedHandler.bind(µm));
})();
/******************************************************************************/
@ -91,6 +85,8 @@
(function() {
var µm = µMatrix;
µm.assets.remoteFetchBarrier += 1;
// This needs to be done when the PSL is loaded
var bindTabs = function(tabs) {
var tab;
@ -102,13 +98,27 @@
µm.bindTabToPageStats(tab.id);
}
µm.webRequest.start();
// Important: remove barrier to remote fetching, this was useful only
// for launch time.
µm.assets.remoteFetchBarrier -= 1;
};
var queryTabs = function() {
vAPI.tabs.getAll(bindTabs);
};
µm.load(queryTabs);
var onSettingsReady = function(settings) {
µm.loadPublicSuffixList(queryTabs);
µm.loadHostsFiles();
};
var onMatrixReady = function() {
};
µm.loadUserSettings(onSettingsReady);
µm.loadMatrix(onMatrixReady);
})();
/******************************************************************************/

View file

@ -19,6 +19,7 @@
Home: https://github.com/gorhill/uMatrix
*/
/* jshint boss: true */
/* global chrome, µMatrix, punycode, publicSuffixList */
/******************************************************************************/
@ -250,7 +251,7 @@
/******************************************************************************/
µMatrix.mergeHostsFile = function(details) {
// console.log('storage.js > mergeHostsFile from "%s": "%s..."', details.path, details.content.slice(0, 40));
//console.log('storage.js > mergeHostsFile from "%s": "%s..."', details.path, details.content.slice(0, 40));
var usedCount = this.ubiquitousBlacklist.count;
var duplicateCount = this.ubiquitousBlacklist.duplicateCount;
@ -268,7 +269,7 @@
/******************************************************************************/
µMatrix.mergeHostsFileContent = function(rawText) {
// console.log('storage.js > mergeHostsFileContent from "%s": "%s..."', details.path, details.content.slice(0, 40));
//console.log('storage.js > mergeHostsFileContent from "%s": "%s..."', details.path, details.content.slice(0, 40));
var rawEnd = rawText.length;
var ubiquitousBlacklist = this.ubiquitousBlacklist;
@ -312,7 +313,7 @@
// For example, when a filter contains whitespace characters, or
// whatever else outside the range of printable ascii characters.
if ( matches[0] !== line ) {
// console.error('"%s": "%s" !== "%s"', details.path, matches[0], line);
//console.error('"%s": "%s" !== "%s"', details.path, matches[0], line);
continue;
}
@ -327,26 +328,50 @@
/******************************************************************************/
// `switches` contains the filter lists for which the switch must be revisited.
µMatrix.selectHostsFiles = function(switches) {
switches = switches || {};
// Only the lists referenced by the switches are touched.
var liveHostsFiles = this.liveHostsFiles;
var entry, state, location;
var i = switches.length;
while ( i-- ) {
entry = switches[i];
state = entry.off === true;
location = entry.location;
if ( liveHostsFiles.hasOwnProperty(location) === false ) {
if ( state !== true ) {
liveHostsFiles[location] = { off: state };
}
continue;
}
if ( liveHostsFiles[location].off === state ) {
continue;
}
liveHostsFiles[location].off = state;
}
vAPI.storage.set({ 'liveHostsFiles': liveHostsFiles });
};
/******************************************************************************/
// `switches` contains the preset blacklists for which the switch must be
// revisited.
µMatrix.reloadHostsFiles = function(switches, update) {
var liveHostsFiles = this.liveHostsFiles;
µMatrix.reloadHostsFiles = function() {
var µm = this;
// Toggle switches
var i = switches.length;
while ( i-- ) {
if ( !liveHostsFiles[switches[i].location] ) {
continue;
}
liveHostsFiles[switches[i].location].off = !!switches[i].off;
}
// We are just reloading the filter lists: we do not want assets to update.
this.assets.autoUpdate = false;
// Save switch states
vAPI.storage.set(
{ 'liveHostsFiles': liveHostsFiles },
this.loadUpdatableAssets.bind(this, update)
);
var onHostsFilesReady = function() {
µm.assets.autoUpdate = µm.userSettings.autoUpdate;
};
this.loadHostsFiles(onHostsFilesReady);
};
/******************************************************************************/
@ -362,86 +387,115 @@
}
callback();
};
this.assets.get(this.pslPath, applyPublicSuffixList);
};
/******************************************************************************/
// Load updatable assets
µMatrix.updateStartHandler = function(callback) {
var µm = this;
var onListsReady = function(lists) {
var assets = {};
for ( var location in lists ) {
if ( lists.hasOwnProperty(location) === false ) {
continue;
}
if ( lists[location].off ) {
continue;
}
assets[location] = true;
}
assets[µm.pslPath] = true;
callback(assets);
};
µMatrix.loadUpdatableAssets = function(forceUpdate, callback) {
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
}
this.assets.autoUpdate = forceUpdate === true;
this.assets.autoUpdateDelay = this.updateAssetsEvery;
if ( forceUpdate ) {
this.updater.restart();
}
this.loadPublicSuffixList(callback);
this.loadHostsFiles();
this.getAvailableHostsFiles(onListsReady);
};
/******************************************************************************/
// Load all
µMatrix.assetUpdatedHandler = function(details) {
var path = details.path || '';
if ( this.liveHostsFiles.hasOwnProperty(path) === false ) {
return;
}
var entry = this.liveHostsFiles[path];
if ( entry.off ) {
return;
}
// Compile the list while we have the raw version in memory
//console.debug('µMatrix.getCompiledFilterList/onRawListLoaded: compiling "%s"', path);
//this.assets.put(
// this.getCompiledFilterListPath(path),
// this.compileFilters(details.content)
//);
};
µMatrix.load = function(callback) {
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
/******************************************************************************/
µMatrix.updateCompleteHandler = function(details) {
var µm = this;
var updatedCount = details.updatedCount;
if ( updatedCount === 0 ) {
return;
}
var µm = this;
var settingsReady = false;
var matrixReady = false;
// Assets are supposed to have been all updated, prevent fetching from
// remote servers.
µm.assets.remoteFetchBarrier += 1;
// TODO: to remove when everybody (and their backup file) has their
// ua-spoof/referrer-spoof setting converted into a matrix switch.
var onSettingsAndMatrixReady = function() {
if ( !settingsReady || !matrixReady ) {
var onFiltersReady = function() {
µm.assets.remoteFetchBarrier -= 1;
};
var onPSLReady = function() {
if ( updatedCount !== 0 ) {
//console.debug('storage.js > µMatrix.updateCompleteHandler: reloading filter lists');
µm.loadHostsFiles(onFiltersReady);
} else {
onFiltersReady();
}
};
if ( details.hasOwnProperty(this.pslPath) ) {
//console.debug('storage.js > µMatrix.updateCompleteHandler: reloading PSL');
this.loadPublicSuffixList(onPSLReady);
updatedCount -= 1;
} else {
onPSLReady();
}
};
/******************************************************************************/
µMatrix.assetCacheRemovedHandler = (function() {
var barrier = false;
var handler = function(paths) {
if ( barrier ) {
return;
}
var saveMatrix = false;
if ( µm.userSettings.spoofUserAgent ) {
µm.tMatrix.setSwitch('ua-spoof', '*', 1);
µm.pMatrix.setSwitch('ua-spoof', '*', 1);
saveMatrix = true;
barrier = true;
var i = paths.length;
var path;
while ( i-- ) {
path = paths[i];
if ( this.liveHostsFiles.hasOwnProperty(path) ) {
//console.debug('µMatrix.assetCacheRemovedHandler: decompiling "%s"', path);
//this.purgeCompiledFilterList(path);
continue;
}
if ( path === this.pslPath ) {
//console.debug('µMatrix.assetCacheRemovedHandler: decompiling "%s"', path);
//this.assets.purge('cache://compiled-publicsuffixlist');
continue;
}
}
if ( µm.userSettings.processReferer ) {
µm.tMatrix.setSwitch('referrer-spoof', '*', 1);
µm.pMatrix.setSwitch('referrer-spoof', '*', 1);
saveMatrix = true;
}
if ( saveMatrix ) {
µm.saveMatrix();
}
delete µm.userSettings.processReferer;
delete µm.userSettings.spoofUserAgent;
µm.saveUserSettings();
µm.XAL.keyvalRemoveOne('processReferer');
µm.XAL.keyvalRemoveOne('spoofUserAgent');
//this.destroySelfie();
barrier = false;
};
var onSettingsReady = function(settings) {
// Never auto-update at boot time
µm.loadUpdatableAssets(false, callback);
// Setup auto-updater, earlier if auto-upate is enabled, later if not
if ( settings.autoUpdate ) {
µm.updater.restart(µm.firstUpdateAfter);
}
settingsReady = true;
onSettingsAndMatrixReady();
};
var onMatrixReady = function() {
matrixReady = true;
onSettingsAndMatrixReady();
};
this.loadUserSettings(onSettingsReady);
this.loadMatrix(onMatrixReady);
this.getBytesInUse();
};
return handler;
})();