Browse Source

assets updater, hosts files pane, etc.

pull/2/head
gorhill 10 years ago
parent
commit
c472e23c7d
  1. 28
      src/_locales/en/messages.json
  2. 1
      src/asset-viewer.html
  3. 1
      src/background.html
  4. 114
      src/css/hosts-files.css
  5. 24
      src/hosts-files.html
  6. 8
      src/js/assets.js
  7. 4
      src/js/background.js
  8. 302
      src/js/hosts-files.js
  9. 1
      src/js/httpsb.js
  10. 72
      src/js/i18n.js
  11. 35
      src/js/messaging.js
  12. 36
      src/js/start.js
  13. 200
      src/js/storage.js

28
src/_locales/en/messages.json

@ -449,6 +449,10 @@
"message": "{{used}} used out of {{total}}", "message": "{{used}} used out of {{total}}",
"description": "" "description": ""
}, },
"hostsFilesLastUpdate" : {
"message":"Last update: {{ago}}",
"description":"English: Last update: {{ago}}, where 'ago' will be replaced with something like '2 days ago'"
},
"hostsFilesApplyChanges" : { "hostsFilesApplyChanges" : {
"message": "Apply changes", "message": "Apply changes",
"description": "" "description": ""
@ -582,6 +586,30 @@
"description": "" "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":{ "errorCantConnectTo":{
"message":"Network error: Unable to connect to {{url}}", "message":"Network error: Unable to connect to {{url}}",

1
src/asset-viewer.html

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

1
src/background.html

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

114
src/css/hosts-files.css

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

24
src/hosts-files.html

@ -27,7 +27,29 @@
<p style="margin: 0.25em 0 0 0"><button id="externalListsParse" disabled="true" data-i18n="hostsFilesExternalListsParse"></button></p> <p style="margin: 0.25em 0 0 0"><button id="externalListsParse" disabled="true" data-i18n="hostsFilesExternalListsParse"></button></p>
</div> </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-common.js"></script>
<script src="js/vapi-client.js"></script> <script src="js/vapi-client.js"></script>

8
src/js/assets.js

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

4
src/js/background.js

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

302
src/js/hosts-files.js

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
µMatrix - a Chromium browser extension to black/white list requests. µ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 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 it under the terms of the GNU General Public License as published by
@ -29,10 +29,25 @@
/******************************************************************************/ /******************************************************************************/
var listDetails = {};
var externalHostsFiles = '';
var cacheWasPurged = false;
var needUpdate = false;
var hasCachedContent = false;
/******************************************************************************/
var onMessage = function(msg) { var onMessage = function(msg) {
switch ( msg.what ) { switch ( msg.what ) {
case 'loadHostsFilesCompleted': case 'loadHostsFilesCompleted':
renderBlacklists();
renderHostsFiles();
break;
case 'forceUpdateAssetsProgress':
renderBusyOverlay(true, msg.progress);
if ( msg.done ) {
messager.send({ what: 'reloadHostsFiles' });
}
break; break;
default: default:
@ -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 // 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 // Assemble a pretty blacklist name if possible
var listNameFromListKey = function(listKey) { var listNameFromListKey = function(listKey) {
@ -70,89 +83,67 @@ var renderBlacklists = function() {
return listTitle; 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 liFromListEntry = function(listKey) {
var elem, text;
var entry = listDetails.available[listKey];
var li = listEntryTemplate.clone();
if ( entry.off !== true ) {
li.descendants('input').attr('checked', '');
} }
var hostname = matches[1];
var domain = hostname;
if ( domain === '' ) {
return '';
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', '');
} }
var html = [
' <a href="http://',
hostname,
'" target="_blank">(',
domain,
')</a>'
];
return html.join('');
};
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');
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 || '';
} }
// 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>'
);
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; needUpdate = true;
} }
} }
// In cache // In cache
if ( asset.cached ) { 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; hasCachedContent = true;
} }
return html.join('\n');
return li;
}; };
var onListsReceived = function(details) { var onListsReceived = function(details) {
@ -161,34 +152,37 @@ var renderBlacklists = function() {
needUpdate = false; needUpdate = false;
hasCachedContent = 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 ( htmlExternal.length !== 0 ) {
htmlBuiltin.push('<li>&nbsp;');
if ( tb === '' ) {
return -1;
}
return 1;
});
var ulList = uDom('#lists').empty();
for ( var i = 0; i < listKeys.length; i++ ) {
ulList.append(liFromListEntry(listKeys[i]));
} }
var html = htmlBuiltin.concat(htmlExternal);
uDom('#listsOfBlockedHostsPrompt').text( 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('#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); 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. // Return whether selection of lists changed.
var listsSelectionChanged = function() { 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 switches = [];
var lis = uDom('#lists .listDetails');
var lis = uDom('#lists .listEntry'), li;
var i = lis.length; var i = lis.length;
var path;
while ( i-- ) { while ( i-- ) {
path = lis
.subset(i, 1)
.descendants('a')
.attr('href');
li = lis.at(i);
switches.push({ 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({ messager.send({
what: 'reloadHostsFiles',
switches: switches,
update: update
});
cacheWasPurged = false;
what: 'selectHostsFiles',
switches: switches
}, callback);
}; };
/******************************************************************************/ /******************************************************************************/
var buttonApplyHandler = function() { 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() { var buttonUpdateHandler = function() {
uDom('#buttonUpdate').removeClass('enabled');
if ( needUpdate ) { if ( needUpdate ) {
reloadAll(true);
renderBusyOverlay(true);
var onSelectionDone = function() {
messager.send({ what: 'forceUpdateAssets' });
};
selectHostsFiles(onSelectionDone);
cacheWasPurged = false;
} }
}; };
/******************************************************************************/ /******************************************************************************/
var buttonPurgeAllHandler = function() { var buttonPurgeAllHandler = function() {
uDom('#buttonPurgeAll').removeClass('enabled');
renderBusyOverlay(true);
var onCompleted = function() { var onCompleted = function() {
renderBlacklists();
cacheWasPurged = true;
renderHostsFiles();
}; };
messager.send({ what: 'purgeAllCaches' }, onCompleted); messager.send({ what: 'purgeAllCaches' }, onCompleted);
}; };
@ -382,7 +422,7 @@ var externalListsApplyHandler = function() {
name: 'externalHostsFiles', name: 'externalHostsFiles',
value: externalHostsFiles value: externalHostsFiles
}); });
renderBlacklists();
renderHostsFiles();
uDom('#externalListsParse').prop('disabled', true); uDom('#externalListsParse').prop('disabled', true);
}; };
@ -393,13 +433,13 @@ uDom.onLoad(function() {
uDom('#buttonApply').on('click', buttonApplyHandler); uDom('#buttonApply').on('click', buttonApplyHandler);
uDom('#buttonUpdate').on('click', buttonUpdateHandler); uDom('#buttonUpdate').on('click', buttonUpdateHandler);
uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler); 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('#lists').on('click', 'span.purge', onPurgeClicked);
uDom('#externalHostsFiles').on('input', externalListsChangeHandler); uDom('#externalHostsFiles').on('input', externalListsChangeHandler);
uDom('#externalListsParse').on('click', externalListsApplyHandler); uDom('#externalListsParse').on('click', externalListsApplyHandler);
renderBlacklists();
renderHostsFiles();
renderExternalLists(); renderExternalLists();
}); });

1
src/js/httpsb.js

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

72
src/js/i18n.js

@ -19,29 +19,65 @@
Home: https://github.com/gorhill/uMatrix 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-- ) {
(function() {
'use strict';
/******************************************************************************/
var nodeList = document.querySelectorAll('[data-i18n]');
var i = nodeList.length;
var node;
while ( i-- ) {
node = nodeList[i]; node = nodeList[i];
vAPI.insertHTML(node, vAPI.i18n(node.getAttribute('data-i18n'))); 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 ) {
}
// copy text of <h1> if any to document title
node = document.querySelector('h1');
if ( node !== null ) {
document.title = node.textContent; document.title = node.textContent;
}
// Tool tips
nodeList = document.querySelectorAll('[data-i18n-tip]');
i = nodeList.length;
while ( i-- ) {
}
// Tool tips
nodeList = document.querySelectorAll('[data-i18n-tip]');
i = nodeList.length;
while ( i-- ) {
node = nodeList[i]; node = nodeList[i];
node.setAttribute('data-tip', vAPI.i18n(node.getAttribute('data-i18n-tip'))); 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');
}
if ( value < 60 ) {
return vAPI.i18n('elapsedManyMinutesAgo').replace('{{value}}', Math.floor(value).toLocaleString());
}
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());
};
/******************************************************************************/
})();
/******************************************************************************/

35
src/js/messaging.js

@ -55,6 +55,10 @@ function onMessage(request, sender, callback) {
µm.forceReload(request.tabId); µm.forceReload(request.tabId);
break; break;
case 'forceUpdateAssets':
µm.assetUpdater.force();
break;
case 'getUserSettings': case 'getUserSettings':
response = µm.userSettings; response = µm.userSettings;
break; break;
@ -68,7 +72,11 @@ function onMessage(request, sender, callback) {
break; break;
case 'reloadHostsFiles': case 'reloadHostsFiles':
µm.reloadHostsFiles(request.switches, request.update);
µm.reloadHostsFiles();
break;
case 'selectHostsFiles':
µm.selectHostsFiles(request.switches);
break; break;
case 'userSettings': 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 getLists = function(callback) {
var r = { var r = {
autoUpdate: µm.userSettings.autoUpdate,
available: null, available: null,
cache: null, cache: null,
current: µm.liveHostsFiles, current: µm.liveHostsFiles,
blockedHostnameCount: µm.ubiquitousBlacklist.count,
autoUpdate: µm.userSettings.autoUpdate
blockedHostnameCount: µm.ubiquitousBlacklist.count
}; };
var onMetadataReady = function(entries) { var onMetadataReady = function(entries) {
r.cache = entries; r.cache = entries;
prepEntries(r.cache);
r.manualUpdate = µm.assetUpdater.manualUpdate;
r.manualUpdateProgress = µm.assetUpdater.manualUpdateProgress;
callback(r); callback(r);
}; };
var onAvailableHostsFilesReady = function(lists) { var onAvailableHostsFilesReady = function(lists) {
r.available = lists; r.available = lists;
prepEntries(r.available);
µm.assets.metadata(onMetadataReady); µm.assets.metadata(onMetadataReady);
}; };
µm.getAvailableHostsFiles(onAvailableHostsFilesReady); µm.getAvailableHostsFiles(onAvailableHostsFilesReady);

36
src/js/start.js

@ -70,18 +70,12 @@
(function() { (function() {
var µm = µMatrix; 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() { (function() {
var µm = µMatrix; var µm = µMatrix;
µm.assets.remoteFetchBarrier += 1;
// This needs to be done when the PSL is loaded // This needs to be done when the PSL is loaded
var bindTabs = function(tabs) { var bindTabs = function(tabs) {
var tab; var tab;
@ -102,13 +98,27 @@
µm.bindTabToPageStats(tab.id); µm.bindTabToPageStats(tab.id);
} }
µm.webRequest.start(); µm.webRequest.start();
// Important: remove barrier to remote fetching, this was useful only
// for launch time.
µm.assets.remoteFetchBarrier -= 1;
}; };
var queryTabs = function() { var queryTabs = function() {
vAPI.tabs.getAll(bindTabs); 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);
})(); })();
/******************************************************************************/ /******************************************************************************/

200
src/js/storage.js

@ -19,6 +19,7 @@
Home: https://github.com/gorhill/uMatrix Home: https://github.com/gorhill/uMatrix
*/ */
/* jshint boss: true */
/* global chrome, µMatrix, punycode, publicSuffixList */ /* global chrome, µMatrix, punycode, publicSuffixList */
/******************************************************************************/ /******************************************************************************/
@ -250,7 +251,7 @@
/******************************************************************************/ /******************************************************************************/
µMatrix.mergeHostsFile = function(details) { µ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 usedCount = this.ubiquitousBlacklist.count;
var duplicateCount = this.ubiquitousBlacklist.duplicateCount; var duplicateCount = this.ubiquitousBlacklist.duplicateCount;
@ -268,7 +269,7 @@
/******************************************************************************/ /******************************************************************************/
µMatrix.mergeHostsFileContent = function(rawText) { µ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 rawEnd = rawText.length;
var ubiquitousBlacklist = this.ubiquitousBlacklist; var ubiquitousBlacklist = this.ubiquitousBlacklist;
@ -312,7 +313,7 @@
// For example, when a filter contains whitespace characters, or // For example, when a filter contains whitespace characters, or
// whatever else outside the range of printable ascii characters. // whatever else outside the range of printable ascii characters.
if ( matches[0] !== line ) { 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; continue;
} }
@ -327,26 +328,50 @@
/******************************************************************************/ /******************************************************************************/
// `switches` contains the preset blacklists for which the switch must be
// revisited.
// `switches` contains the filter lists for which the switch must be revisited.
µMatrix.reloadHostsFiles = function(switches, update) {
var liveHostsFiles = this.liveHostsFiles;
µMatrix.selectHostsFiles = function(switches) {
switches = switches || {};
// Toggle switches
// Only the lists referenced by the switches are touched.
var liveHostsFiles = this.liveHostsFiles;
var entry, state, location;
var i = switches.length; var i = switches.length;
while ( i-- ) { while ( i-- ) {
if ( !liveHostsFiles[switches[i].location] ) {
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; continue;
} }
liveHostsFiles[switches[i].location].off = !!switches[i].off;
liveHostsFiles[location].off = state;
} }
// Save switch states
vAPI.storage.set(
{ 'liveHostsFiles': liveHostsFiles },
this.loadUpdatableAssets.bind(this, update)
);
vAPI.storage.set({ 'liveHostsFiles': liveHostsFiles });
};
/******************************************************************************/
// `switches` contains the preset blacklists for which the switch must be
// revisited.
µMatrix.reloadHostsFiles = function() {
var µm = this;
// We are just reloading the filter lists: we do not want assets to update.
this.assets.autoUpdate = false;
var onHostsFilesReady = function() {
µm.assets.autoUpdate = µm.userSettings.autoUpdate;
};
this.loadHostsFiles(onHostsFilesReady);
}; };
/******************************************************************************/ /******************************************************************************/
@ -362,86 +387,115 @@
} }
callback(); callback();
}; };
this.assets.get(this.pslPath, applyPublicSuffixList); this.assets.get(this.pslPath, applyPublicSuffixList);
}; };
/******************************************************************************/ /******************************************************************************/
// Load updatable assets
µMatrix.loadUpdatableAssets = function(forceUpdate, callback) {
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
µMatrix.updateStartHandler = function(callback) {
var µm = this;
var onListsReady = function(lists) {
var assets = {};
for ( var location in lists ) {
if ( lists.hasOwnProperty(location) === false ) {
continue;
} }
this.assets.autoUpdate = forceUpdate === true;
this.assets.autoUpdateDelay = this.updateAssetsEvery;
if ( forceUpdate ) {
this.updater.restart();
if ( lists[location].off ) {
continue;
} }
assets[location] = true;
}
assets[µm.pslPath] = true;
callback(assets);
};
this.loadPublicSuffixList(callback);
this.loadHostsFiles();
this.getAvailableHostsFiles(onListsReady);
}; };
/******************************************************************************/ /******************************************************************************/
// Load all
µMatrix.load = function(callback) {
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
µ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.updateCompleteHandler = function(details) {
var µm = this; var µm = this;
var settingsReady = false;
var matrixReady = false;
// 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 updatedCount = details.updatedCount;
if ( updatedCount === 0 ) {
return; return;
} }
var saveMatrix = false;
if ( µm.userSettings.spoofUserAgent ) {
µm.tMatrix.setSwitch('ua-spoof', '*', 1);
µm.pMatrix.setSwitch('ua-spoof', '*', 1);
saveMatrix = true;
}
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');
};
var onSettingsReady = function(settings) {
// Never auto-update at boot time
µm.loadUpdatableAssets(false, callback);
// Assets are supposed to have been all updated, prevent fetching from
// remote servers.
µm.assets.remoteFetchBarrier += 1;
// Setup auto-updater, earlier if auto-upate is enabled, later if not
if ( settings.autoUpdate ) {
µm.updater.restart(µm.firstUpdateAfter);
}
settingsReady = true;
onSettingsAndMatrixReady();
var onFiltersReady = function() {
µm.assets.remoteFetchBarrier -= 1;
}; };
var onMatrixReady = function() {
matrixReady = true;
onSettingsAndMatrixReady();
var onPSLReady = function() {
if ( updatedCount !== 0 ) {
//console.debug('storage.js > µMatrix.updateCompleteHandler: reloading filter lists');
µm.loadHostsFiles(onFiltersReady);
} else {
onFiltersReady();
}
}; };
this.loadUserSettings(onSettingsReady);
this.loadMatrix(onMatrixReady);
this.getBytesInUse();
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;
}
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;
}
}
//this.destroySelfie();
barrier = false;
};
return handler;
})();
Loading…
Cancel
Save