Browse Source

work on many things, need more

pull/2/head
gorhill 10 years ago
parent
commit
2b41bc8087
  1. 26
      src/about.html
  2. 19
      src/asset-viewer.html
  3. 1
      src/background.html
  4. 8
      src/css/dashboard-common.css
  5. 115
      src/css/hosts-files.css
  6. 4
      src/css/popup.css
  7. 9
      src/dashboard.html
  8. 56
      src/hosts-files.html
  9. 124
      src/js/about.js
  10. 848
      src/js/assets.js
  11. 23
      src/js/background.js
  12. 416
      src/js/hosts-files.js
  13. 2
      src/js/httpsb.js
  14. 3
      src/js/liquid-dict.js
  15. 49
      src/js/messaging-handlers.js
  16. 8
      src/js/messaging.js
  17. 269
      src/js/storage.js
  18. 2
      src/js/traffic.js
  19. 30
      src/js/udom.js
  20. 8
      tools/_locales/de/messages.json
  21. 76
      tools/_locales/en/messages.json
  22. 8
      tools/_locales/fr/messages.json
  23. 8
      tools/_locales/ru/messages.json
  24. 8
      tools/_locales/zh_CN/messages.json

26
src/about.html

@ -66,34 +66,12 @@ table td:first-child {
<div> <div>
<p><button type="button" id="backupUserDataButton" data-i18n="aboutUserDataBackupButton"></button> <p><button type="button" id="backupUserDataButton" data-i18n="aboutUserDataBackupButton"></button>
<button type="button" id="restoreUserDataButton" data-i18n="aboutUserDataRestoreButton"></button> <button type="button" id="restoreUserDataButton" data-i18n="aboutUserDataRestoreButton"></button>
<input id="restoreFilePicker" type="file" accept="text/plain" style="display:none;">
<p style="margin-left: 2em;" data-i18n="aboutUserDataOr"> <p style="margin-left: 2em;" data-i18n="aboutUserDataOr">
<p><button type="button" id="resetUserDataButton" data-i18n="aboutUserDataResetButton"></button> <p><button type="button" id="resetUserDataButton" data-i18n="aboutUserDataResetButton"></button>
</div> </div>
<h2 data-i18n="aboutExtensionDataHeader"></h2>
<div>
<p class="para" data-i18n="aboutAssetsUpdatePrompt"></p>
<div id="assetList">
<!--
Let's define 'abc' as bit 0 to 2
Where bit can be '0' or '1'
Bit 0: list => o=absent, x=present
Bit 1: list => o=clean, x=dirty
Bit 2: update => o=idle, x=updating
Therefore:
List visible: oox oxx xox
Etc.
-->
<table class="ooo">
<tr><th data-i18n="aboutAssetsUpdateColPath"><th data-i18n="aboutAssetsUpdateColStatus">
</table>
<p class="ooo" style="color:red" data-i18n="aboutAssetsUpdateGetListError"></p>
<p class="oxx"><button type="button" id="aboutAssetsUpdateButton" data-i18n="aboutAssetsUpdateButton"></button></p>
<p class="xxx"><button type="button" disabled data-i18n="aboutAssetsUpdatingButton"></button></p>
</div>
</div>
<script src="lib/jquery-2.min.js"></script>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script> <script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script> <script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.js"></script> <script src="js/messaging-client.js"></script>

19
src/asset-viewer.html

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>µMatrix — Asset</title>
<style>
#content {
font: 12px monospace;
white-space: pre;
}
</style>
</head>
<body>
<div id="content"></div>
<script src="js/udom.js"></script>
<script src="js/messaging-client.js"></script>
<script src="js/asset-viewer.js"></script>
</body>
</html>

1
src/background.html

@ -17,7 +17,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/asset-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>

8
src/css/dashboard-common.css

@ -1,7 +1,10 @@
body { body {
margin: 0; margin: 0;
padding: 0 0.5em 5em 0.5em; padding: 0 0.5em 5em 0.5em;
font: 15px httpsb,sans-serif;
font: 15px sans-serif;
}
body > *:first-child {
margin-top: 0;
} }
h2, h3 { h2, h3 {
margin: 1em 0; margin: 1em 0;
@ -24,7 +27,7 @@ a {
text-decoration: none; text-decoration: none;
} }
button { button {
padding: 0.4em;
padding: 0.3em 0.5em;
} }
.para { .para {
@ -49,6 +52,7 @@ button {
.whatisthis-expandable { .whatisthis-expandable {
margin: 0.5em 0 1em 1.25em; margin: 0.5em 0 1em 1.25em;
padding: 0.5em; padding: 0.5em;
font-size: smaller;
display: none; display: none;
border: 1px dotted black; border: 1px dotted black;
background-color: #F8F8F8; background-color: #F8F8F8;

115
src/css/hosts-files.css

@ -0,0 +1,115 @@
ul {
padding: 0;
list-style-type: none;
}
ul#options {
margin-top: 0;
}
ul#options li {
margin-bottom: 0.5em;
}
ul#lists {
margin: 0.5em 0 0 0;
padding-__MSG_@@bidi_end_edge__: 0em;
padding-__MSG_@@bidi_start_edge__: 1em;
}
li.listDetails {
font-size: 14px;
margin: 0 auto 0 auto;
margin-__MSG_@@bidi_start_edge__: 1em;
margin-__MSG_@@bidi_end_edge__: 0em;
}
li.listDetails > * {
unicode-bidi: embed;
}
li.listDetails > a:nth-of-type(2) {
font-size: 13px;
opacity: 0.5;
}
.dim {
opacity: 0.5;
}
/* I designed the button with: http://charliepark.org/bootstrap_buttons/ */
button.custom {
padding: 5px;
border: 1px solid transparent;
border-color: #80b3ff #80b3ff hsl(216, 100%, 75%);
border-radius: 3px;
background-color: hsl(216, 100%, 75%);
background-image: linear-gradient(#a8cbff, #80b3ff);
background-repeat: repeat-x;
color: #222;
cursor: pointer;
opacity: 0.8;
}
button.custom.disabled {
border-color: #dddddd #dddddd hsl(36, 0%, 85%);
background-color: hsl(36, 0%, 72%);
background-image: linear-gradient(#f2f2f2, #dddddd);
color: #aaa;
pointer-events: none;
}
button.custom:hover {
opacity: 1.0;
}
button.custom.reloadAll:not(.disabled) {
border-color: #ffcc7f #ffcc7f hsl(36, 100%, 73%);
background-color: hsl(36, 100%, 75%);
background-image: linear-gradient(#ffdca8, #ffcc7f);
}
#buttonApply {
position: fixed;
display: initial;
top: 1em;
__MSG_@@bidi_end_edge__: 1em;
}
#buttonApply.disabled {
display: none;
}
span.status {
margin: 0;
border: 1px solid transparent;
padding: 1px 2px;
display: inline-block;
font-size: 11px;
opacity: 0.7;
}
span.purge {
border-color: #ddd;
color: #444;
background-color: #eee;
cursor: pointer;
}
span.purge:hover {
opacity: 1;
}
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;
}
#externalHostsFiles {
font-size: smaller;
width: 48em;
height: 12em;
white-space: nowrap;
}
body #busyOverlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: white;
opacity: 0.5;
cursor: wait;
display: none;
z-index: 1000;
}
body.busy #busyOverlay {
display: block;
}

4
src/css/popup.css

@ -438,12 +438,12 @@ body.colorblind .t82 {
body.colorblind .t1 { body.colorblind .t1 {
border-color: #333; border-color: #333;
color: white; color: white;
background-color: #555;
background-color: #666;
} }
body.colorblind .t2 { body.colorblind .t2 {
border-color: #aaa; border-color: #aaa;
color: black; color: black;
background-color: #ddd;
background-color: #ccc;
} }
body.colorblind .matCell.p81 { body.colorblind .matCell.p81 {
background-image: url('/img/permanent-black-small-cb.png'); background-image: url('/img/permanent-black-small-cb.png');

9
src/dashboard.html

@ -8,7 +8,7 @@ body {
margin: 0; margin: 0;
border: 0; border: 0;
padding: 0; padding: 0;
font: 15px httpsb,sans-serif;
font: 15px sans-serif;
position: relative; position: relative;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
@ -18,7 +18,6 @@ body {
margin: 0; margin: 0;
border: 0; border: 0;
padding: 0; padding: 0;
position: absolute;
top: 0; top: 0;
width: 100vw; width: 100vw;
height: 50px; height: 50px;
@ -27,7 +26,8 @@ body {
#dashboard-nav-widgets { #dashboard-nav-widgets {
margin: 0; margin: 0;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
padding: 4px 0 3px 0;
padding: 4px 0;
box-sizing: border-box;
white-space: nowrap; white-space: nowrap;
background-color: white; background-color: white;
} }
@ -41,6 +41,7 @@ body {
border-top-left-radius: 3px; border-top-left-radius: 3px;
border-top-right-radius: 3px; border-top-right-radius: 3px;
padding: 4px; padding: 4px;
box-sizing: border-box;
color: black; color: black;
background-color: #eee; background-color: #eee;
font: inherit; font: inherit;
@ -79,7 +80,7 @@ iframe {
<a class="tabButton" id="settings" href="#settings" data-dashboard-panel-url="settings.html" data-i18n="settingsPageName"></a> <a class="tabButton" id="settings" href="#settings" data-dashboard-panel-url="settings.html" data-i18n="settingsPageName"></a>
<a class="tabButton" id="privacy" href="#privacy" data-dashboard-panel-url="privacy.html" data-i18n="privacyPageName"></a> <a class="tabButton" id="privacy" href="#privacy" data-dashboard-panel-url="privacy.html" data-i18n="privacyPageName"></a>
<a class="tabButton" id="user-rules" href="#user-rules" data-dashboard-panel-url="user-rules.html" data-i18n="userRulesPageName"></a> <a class="tabButton" id="user-rules" href="#user-rules" data-dashboard-panel-url="user-rules.html" data-i18n="userRulesPageName"></a>
<a class="tabButton" id="ubiquitous-rules" href="#ubiquitous-rules" data-dashboard-panel-url="ubiquitous-rules.html" data-i18n="ubiquitousRulesPageName"></a>
<a class="tabButton" id="hosts-files" href="#hosts-files" data-dashboard-panel-url="hosts-files.html" data-i18n="ubiquitousRulesPageName"></a>
<a class="tabButton" id="statistics" href="#statistics" data-dashboard-panel-url="info.html" data-i18n="statsPageName"></a> <a class="tabButton" id="statistics" href="#statistics" data-dashboard-panel-url="info.html" data-i18n="statsPageName"></a>
<a class="tabButton" id="about" href="#about" data-dashboard-panel-url="about.html" data-i18n="aboutPageName"></a> <a class="tabButton" id="about" href="#about" data-dashboard-panel-url="about.html" data-i18n="aboutPageName"></a>
</div> </div>

56
src/hosts-files.html

@ -2,55 +2,39 @@
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>µMatrix — Ubiquitous rules</title>
<title>HTTP Switchboard — Ubiquitous rules</title>
<link rel="stylesheet" type="text/css" href="css/common.css"> <link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css"> <link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
<style>
div > p:first-child {
margin-top: 0;
}
div > p:last-child {
margin-bottom: 0;
}
ul {
padding: 0;
list-style-type: none;
}
ul > li {
margin: 0 0 0 1em;
}
.dim {
color: #444;
}
</style>
<link rel="stylesheet" type="text/css" href="css/hosts-files.css">
</head> </head>
<body> <body>
<h2 data-i18n="ubiquitousWhatIsThisHeader"></h2>
<div>
<p data-i18n="ubiquitousWhatIsThisPrompt"></p>
</div>
<p data-i18n="hostsFilesPrompt"></p>
<ul id="options">
<li><input type="checkbox" id="autoUpdate"><label data-i18n="hostsFilesAutoUpdatePrompt" for="autoUpdate"></label>
<button class="custom reloadAll disabled" id="buttonUpdate" data-i18n="hostsFilesUpdateNow"></button>
<button id="buttonPurgeAll" class="custom disabled" data-i18n="hostsFilesPurgeAll"></button>
<li><p id="listsOfBlockedHostsPrompt"></p>
</ul>
<button id="buttonApply" class="custom reloadAll disabled" data-i18n="hostsFilesApplyChanges"></button>
<ul id="lists">
</ul>
<h2 data-i18n="ubiquitousListsOfBlockedHostsHeader"></h2>
<div>
<p data-i18n="ubiquitousListsOfBlockedHostsPrompt1"></p>
<p id="ubiquitousListsOfBlockedHostsPrompt2"></p>
<ul id="blacklistTemplate" style="display:none">
<li class="blacklistDetails"><input type="checkbox">&thinsp;<a href="" type="text/plain"></a>:
<span class="dim" data-i18n="ubiquitousListsOfBlockedHostsPerListStats"></span>
</ul>
<ul id="blacklists" style="margin:0.5em 0 0 0;padding-left:1em;list-style-type:none">
<li style="margin-top:0.75em"><button id="blacklistsApply" disabled="true" data-i18n="ubiquitousApplyChanges"></button>
</ul>
<div id="externalListsDiv">
<p data-i18n="hostsFilesExternalListsHint" style="margin: 0 0 0.25em 0; font-size: 13px;"></p>
<textarea id="externalHostsFiles" dir="ltr" spellcheck="false"></textarea>
<p style="margin: 0.25em 0 0 0"><button id="externalListsParse" disabled="true" data-i18n="hostsFilesExternalListsParse"></button></p>
</div> </div>
<script src="lib/jquery-2.min.js"></script>
<div id="busyOverlay"></div>
<script src="js/udom.js"></script> <script src="js/udom.js"></script>
<script src="js/i18n.js"></script> <script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script> <script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.js"></script> <script src="js/messaging-client.js"></script>
<script src="js/ubiquitous-rules.js"></script>
<script src="js/hosts-files.js"></script>
</body> </body>
</html> </html>

124
src/js/about.js

@ -19,17 +19,11 @@
Home: https://github.com/gorhill/uMatrix Home: https://github.com/gorhill/uMatrix
*/ */
/* global chrome, $ */
/* global chrome, uDom */
/******************************************************************************/ /******************************************************************************/
$(function() {
/******************************************************************************/
var updateList = {};
var assetListSwitches = ['o', 'o', 'o'];
var commitHistoryURLPrefix = 'https://github.com/gorhill/httpswitchboard/commits/master/';
uDom.onLoad(function() {
/******************************************************************************/ /******************************************************************************/
@ -75,12 +69,7 @@ var backupUserDataToFile = function() {
/******************************************************************************/ /******************************************************************************/
var restoreUserDataFromFile = function() {
var input = $('<input />').attr({
type: 'file',
accept: 'text/plain'
});
function restoreUserDataFromFile() {
var restartCountdown = 4; var restartCountdown = 4;
var doCountdown = function() { var doCountdown = function() {
restartCountdown -= 1; restartCountdown -= 1;
@ -151,10 +140,8 @@ var restoreUserDataFromFile = function() {
} }
}; };
var filePickerOnChangeHandler = function() {
$(this).off('change', filePickerOnChangeHandler);
var file = this.files[0]; var file = this.files[0];
if ( !file ) {
if ( file === undefined || file.name === '' ) {
return; return;
} }
if ( file.type.indexOf('text') !== 0 ) { if ( file.type.indexOf('text') !== 0 ) {
@ -163,11 +150,17 @@ var restoreUserDataFromFile = function() {
var fr = new FileReader(); var fr = new FileReader();
fr.onload = fileReaderOnLoadHandler; fr.onload = fileReaderOnLoadHandler;
fr.readAsText(file); fr.readAsText(file);
input.off('change', filePickerOnChangeHandler);
};
}
/******************************************************************************/
input.on('change', filePickerOnChangeHandler);
input.trigger('click');
var startRestoreFilePicker = function() {
var input = document.getElementById('restoreFilePicker');
// Reset to empty string, this will ensure an change event is properly
// triggered if the user pick a file, even if it is the same as the last
// one picked.
input.value = '';
input.click();
}; };
/******************************************************************************/ /******************************************************************************/
@ -181,106 +174,29 @@ var resetUserData = function() {
/******************************************************************************/ /******************************************************************************/
var setAssetListClassBit = function(bit, state) {
assetListSwitches[assetListSwitches.length-1-bit] = !state ? 'o' : 'x';
$('#assetList')
.removeClass()
.addClass(assetListSwitches.join(''));
};
/******************************************************************************/
var renderAssetList = function(details) {
var dirty = false;
var paths = Object.keys(details.list).sort();
if ( paths.length > 0 ) {
$('#assetList .assetEntry').remove();
var assetTable = $('#assetList table');
var i = 0;
var path, status, html;
while ( path = paths[i++] ) {
status = details.list[path].status;
dirty = dirty || status !== 'Unchanged';
html = [];
html.push('<tr class="assetEntry ' + status.toLowerCase().replace(/ +/g, '-') + '">');
html.push('<td>');
html.push('<a href="' + commitHistoryURLPrefix + path + '">');
html.push(path.replace(/^(assets\/[^/]+\/)(.+)$/, '$1<b>$2</b>'));
html.push('</a>');
html.push('<td>');
html.push(chrome.i18n.getMessage('aboutAssetsUpdateStatus' + status));
assetTable.append(html.join(''));
}
$('#assetList a').attr('target', '_blank');
updateList = details.list;
}
setAssetListClassBit(0, paths.length !== 0);
setAssetListClassBit(1, dirty);
setAssetListClassBit(2, false);
};
/******************************************************************************/
var updateAssets = function() {
setAssetListClassBit(2, true);
var onDone = function(details) {
if ( details.changedCount !== 0 ) {
messaging.tell({ what: 'loadUpdatableAssets' });
}
};
messaging.ask({ what: 'launchAssetUpdater', list: updateList }, onDone);
};
/******************************************************************************/
var updateAssetsList = function() {
messaging.ask({ what: 'getAssetUpdaterList' }, renderAssetList);
};
/******************************************************************************/
// Updating all assets could be done from elsewhere and if so the
// list here needs to be updated.
var onAnnounce = function(msg) {
switch ( msg.what ) {
case 'allLocalAssetsUpdated':
updateAssetsList();
break;
default:
break;
}
};
messaging.start('about.js'); messaging.start('about.js');
messaging.listen(onAnnounce);
/******************************************************************************/ /******************************************************************************/
(function() { (function() {
$('#aboutVersion').html(chrome.runtime.getManifest().version);
uDom('#aboutVersion').html(chrome.runtime.getManifest().version);
var renderStats = function(details) { var renderStats = function(details) {
var template = chrome.i18n.getMessage('aboutStorageUsed'); var template = chrome.i18n.getMessage('aboutStorageUsed');
var percent = 0; var percent = 0;
if ( details.storageQuota ) { if ( details.storageQuota ) {
percent = (details.storageUsed / details.storageQuota * 100).toFixed(1); percent = (details.storageUsed / details.storageQuota * 100).toFixed(1);
} }
$('#aboutStorageUsed').html(template.replace('{{storageUsed}}', percent));
uDom('#aboutStorageUsed').html(template.replace('{{storageUsed}}', percent));
}; };
messaging.ask({ what: 'getSomeStats' }, renderStats); messaging.ask({ what: 'getSomeStats' }, renderStats);
})(); })();
/******************************************************************************/ /******************************************************************************/
$('#aboutAssetsUpdateButton').on('click', updateAssets);
$('#backupUserDataButton').on('click', backupUserDataToFile);
$('#restoreUserDataButton').on('click', restoreUserDataFromFile);
$('#resetUserDataButton').on('click', resetUserData);
/******************************************************************************/
updateAssetsList();
uDom('#backupUserDataButton').on('click', backupUserDataToFile);
uDom('#restoreUserDataButton').on('click', startRestoreFilePicker);
uDom('#restoreFilePicker').on('change', restoreUserDataFromFile);
uDom('#resetUserDataButton').on('click', resetUserData);
/******************************************************************************/ /******************************************************************************/

848
src/js/assets.js
File diff suppressed because it is too large
View File

23
src/js/background.js

@ -45,6 +45,7 @@ return {
manifest: chrome.runtime.getManifest(), manifest: chrome.runtime.getManifest(),
userSettings: { userSettings: {
autoUpdate: false,
clearBrowserCache: true, clearBrowserCache: true,
clearBrowserCacheAfter: 60, clearBrowserCacheAfter: 60,
colorBlindFriendly: false, colorBlindFriendly: false,
@ -53,11 +54,12 @@ return {
deleteUnusedSessionCookiesAfter: 60, deleteUnusedSessionCookiesAfter: 60,
deleteLocalStorage: false, deleteLocalStorage: false,
displayTextSize: '13px', displayTextSize: '13px',
externalHostsFiles: '',
maxLoggedRequests: 50, maxLoggedRequests: 50,
popupCollapseDomains: false, popupCollapseDomains: false,
popupCollapseSpecificDomains: {}, popupCollapseSpecificDomains: {},
popupHideBlacklisted: false, popupHideBlacklisted: false,
popupScopeLevel: '*',
popupScopeLevel: 'domain',
processBehindTheSceneRequests: false, processBehindTheSceneRequests: false,
processHyperlinkAuditing: true, processHyperlinkAuditing: true,
processReferer: false, processReferer: false,
@ -74,12 +76,16 @@ return {
updateAssetsEvery: 5 * 24 * 60 * 60 * 1000, updateAssetsEvery: 5 * 24 * 60 * 60 * 1000,
projectServerRoot: 'https://raw.githubusercontent.com/gorhill/umatrix/master/', projectServerRoot: 'https://raw.githubusercontent.com/gorhill/umatrix/master/',
// list of remote blacklist locations
remoteBlacklists: {
// uMatrix
'assets/umatrix/blacklist.txt': { title: 'uMatrix' },
// permanent hosts files
permanentHostsFiles: {
// µMatrix
'assets/umatrix/blacklist.txt': {
title: 'µMatrix hosts file'
}
},
// 3rd-party lists now fetched dynamically
// list of live hosts files
liveHostsFiles: {
}, },
// urls stats are kept on the back burner while waiting to be reactivated // urls stats are kept on the back burner while waiting to be reactivated
@ -99,10 +105,7 @@ return {
tMatrix: null, tMatrix: null,
pMatrix: null, pMatrix: null,
// Current entries from ubiquitous lists --
// just hostnames, '*/' is implied, this saves significantly on memory.
ubiquitousBlacklist: null, ubiquitousBlacklist: null,
ubiquitousWhitelist: null,
// various stats // various stats
requestStats: new WebRequestStats(), requestStats: new WebRequestStats(),
@ -128,6 +131,8 @@ return {
noopCSSURL: chrome.runtime.getURL('css/noop.css'), noopCSSURL: chrome.runtime.getURL('css/noop.css'),
fontCSSURL: chrome.runtime.getURL('css/fonts/Roboto_Condensed/RobotoCondensed-Regular.ttf'), fontCSSURL: chrome.runtime.getURL('css/fonts/Roboto_Condensed/RobotoCondensed-Regular.ttf'),
noopFunc: function(){},
// so that I don't have to care for last comma // so that I don't have to care for last comma
dummy: 0 dummy: 0
}; };

416
src/js/hosts-files.js

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uMatrix Home: https://github.com/gorhill/uMatrix
*/ */
/* global chrome, $ */
/* global chrome, messaging, uDom */
/******************************************************************************/ /******************************************************************************/
@ -27,17 +27,23 @@
/******************************************************************************/ /******************************************************************************/
var selectedBlacklistsHash = '';
var listDetails = {};
var externalHostsFiles = '';
var cacheWasPurged = false;
var needUpdate = false;
var hasCachedContent = false;
var re3rdPartyExternalAsset = /^https?:\/\/[a-z0-9]+/;
var re3rdPartyRepoAsset = /^assets\/thirdparties\/([^\/]+)/;
/******************************************************************************/ /******************************************************************************/
messaging.start('ubiquitous-rules.js');
messaging.start('hosts-files.js');
var onMessage = function(msg) { var onMessage = function(msg) {
switch ( msg.what ) { switch ( msg.what ) {
case 'loadUbiquitousBlacklistCompleted':
case 'loadHostsFilesCompleted':
renderBlacklists(); renderBlacklists();
selectedBlacklistsChanged();
break; break;
default: default:
@ -49,152 +55,352 @@ messaging.listen(onMessage);
/******************************************************************************/ /******************************************************************************/
function getµm() {
return chrome.extension.getBackgroundPage().µMatrix;
}
/******************************************************************************/
function changeUserSettings(name, value) {
messaging.tell({
what: 'userSettings',
name: name,
value: value
});
}
/******************************************************************************/
// TODO: get rid of background page dependencies // TODO: get rid of background page dependencies
function renderBlacklists() {
// empty list first
$('#blacklists .blacklistDetails').remove();
var µm = getµm();
$('#ubiquitousListsOfBlockedHostsPrompt2').text(
chrome.i18n.getMessage('ubiquitousListsOfBlockedHostsPrompt2')
.replace('{{ubiquitousBlacklistCount}}', µm.ubiquitousBlacklist.count.toLocaleString())
);
var renderBlacklists = function() {
uDom('body').toggleClass('busy', true);
// Assemble a pretty blacklist name if possible // Assemble a pretty blacklist name if possible
var prettifyListName = function(blacklistTitle, blacklistHref) {
if ( !blacklistTitle ) {
return blacklistHref;
var listNameFromListKey = function(listKey) {
var list = listDetails.current[listKey] || listDetails.available[listKey];
var listTitle = list ? list.title : '';
if ( listTitle === '' ) {
return listKey;
} }
return listTitle;
};
// Assemble a pretty blacklist name if possible
var htmlFromHomeURL = function(blacklistHref) {
if ( blacklistHref.indexOf('assets/thirdparties/') !== 0 ) { if ( blacklistHref.indexOf('assets/thirdparties/') !== 0 ) {
return blacklistTitle;
return '';
} }
var matches = blacklistHref.match(/^assets\/thirdparties\/([^\/]+)/);
var matches = re3rdPartyRepoAsset.exec(blacklistHref);
if ( matches === null || matches.length !== 2 ) { if ( matches === null || matches.length !== 2 ) {
return blacklistTitle;
return '';
} }
var hostname = matches[1]; var hostname = matches[1];
var domain = µm.URI.domainFromHostname(hostname);
var domain = hostname;
if ( domain === '' ) { if ( domain === '' ) {
return blacklistTitle;
return '';
} }
var html = [ var html = [
blacklistTitle,
' <i>(<a href="http://',
' <a href="http://',
hostname, hostname,
'" target="_blank">',
'" target="_blank">(',
domain, domain,
'</a>)</i>'
')</a>'
]; ];
return html.join(''); return html.join('');
}; };
var blacklists = µm.remoteBlacklists;
var ul = $('#blacklists');
var keys = Object.keys(blacklists);
var i = keys.length;
var blacklist, blacklistHref;
var liTemplate = $('#blacklistTemplate .blacklistDetails').first();
var li, child, text;
while ( i-- ) {
blacklistHref = keys[i];
blacklist = blacklists[blacklistHref];
li = liTemplate.clone();
child = $('input', li);
child.prop('checked', !blacklist.off);
child = $('a', li);
child.attr('href', encodeURI(blacklistHref));
child.html(prettifyListName(blacklist.title, blacklistHref));
child = $('span', li);
text = child.text()
.replace('{{used}}', !blacklist.off && !isNaN(+blacklist.entryUsedCount) ? blacklist.entryUsedCount.toLocaleString() : '0')
.replace('{{total}}', !isNaN(+blacklist.entryCount) ? blacklist.entryCount.toLocaleString() : '?')
;
child.text(text);
ul.prepend(li);
}
selectedBlacklistsHash = getSelectedBlacklistsHash();
}
/******************************************************************************/
// Create a hash so that we know whether the selection of preset blacklists
// has changed.
function getSelectedBlacklistsHash() {
var hash = '';
var inputs = $('#blacklists .blacklistDetails > input');
var i = inputs.length;
var input, entryHash;
while ( i-- ) {
input = $(inputs[i]);
entryHash = input.prop('checked').toString();
hash += entryHash;
var purgeButtontext = chrome.i18n.getMessage('hostsFilesExternalListPurge');
var updateButtontext = chrome.i18n.getMessage('hostsFilesExternalListNew');
var obsoleteButtontext = chrome.i18n.getMessage('hostsFilesExternalListObsolete');
var liTemplate = [
'<li class="listDetails">',
'<input type="checkbox" {{checked}}>',
' ',
'<a href="{{URL}}" type="text/plain">',
'{{name}}',
'\u200E</a>',
'{{homeURL}}',
': ',
'<span class="dim">',
chrome.i18n.getMessage('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');
}
// 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>'
);
needUpdate = true;
}
}
// In cache
if ( asset.cached ) {
html.push(
'&ensp;',
'<span class="status purge">',
purgeButtontext,
'</span>'
);
hasCachedContent = true;
} }
return html.join('\n');
};
return hash;
}
var onListsReceived = function(details) {
// Before all, set context vars
listDetails = details;
needUpdate = false;
hasCachedContent = false;
// Visually split the filter lists in two groups: built-in and external
var html = [];
var hostsPaths = Object.keys(details.available);
var hostsEntry;
for ( i = 0; i < hostsPaths.length; i++ ) {
hostsPath = hostsPaths[i];
hostsEntry = details.available[hostsPath];
if ( !hostsEntry.external ) {
html.push(htmlFromLeaf(hostsPath, hostsEntry));
}
}
for ( i = 0; i < hostsPaths.length; i++ ) {
hostsPath = hostsPaths[i];
hostsEntry = details.available[hostsPath];
if ( hostsEntry.external ) {
html.push(htmlFromLeaf(hostsPath, hostsEntry));
}
}
uDom('#listsOfBlockedHostsPrompt').text(
chrome.i18n.getMessage('hostsFilesStats')
.replace('{{blockedHostnameCount}}', details.blockedHostnameCount.toLocaleString())
);
uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
uDom('#lists').html(html.join(''));
uDom('a').attr('target', '_blank');
updateWidgets();
};
messaging.ask({ what: 'getLists' }, onListsReceived);
};
/******************************************************************************/
// Return whether selection of lists changed.
var listsSelectionChanged = function() {
if ( cacheWasPurged ) {
return true;
}
var availableLists = listDetails.available;
var currentLists = listDetails.current;
var location, availableOff, currentOff;
// This check existing entries
for ( location in availableLists ) {
if ( availableLists.hasOwnProperty(location) === false ) {
continue;
}
availableOff = availableLists[location].off === true;
currentOff = currentLists[location] === undefined || currentLists[location].off === true;
if ( availableOff !== currentOff ) {
return true;
}
}
// This check removed entries
for ( location in currentLists ) {
if ( currentLists.hasOwnProperty(location) === false ) {
continue;
}
currentOff = currentLists[location].off === true;
availableOff = availableLists[location] === undefined || availableLists[location].off === true;
if ( availableOff !== currentOff ) {
return true;
}
}
return false;
};
/******************************************************************************/
// Return whether content need update.
var listsContentChanged = function() {
return needUpdate;
};
/******************************************************************************/ /******************************************************************************/
// This is to give a visual hint that the selection of blacklists has changed. // This is to give a visual hint that the selection of blacklists has changed.
function selectedBlacklistsChanged() {
$('#blacklistsApply').attr(
'disabled',
getSelectedBlacklistsHash() === selectedBlacklistsHash
);
}
var updateWidgets = function() {
uDom('#buttonApply').toggleClass('disabled', !listsSelectionChanged());
uDom('#buttonUpdate').toggleClass('disabled', !listsContentChanged());
uDom('#buttonPurgeAll').toggleClass('disabled', !hasCachedContent);
uDom('body').toggleClass('busy', false);
};
/******************************************************************************/ /******************************************************************************/
function blacklistsApplyHandler() {
var newHash = getSelectedBlacklistsHash();
if ( newHash === selectedBlacklistsHash ) {
var onListCheckboxChanged = function() {
var href = uDom(this).parent().descendants('a').first().attr('href');
if ( typeof href !== 'string' ) {
return;
}
if ( listDetails.available[href] === undefined ) {
return; return;
} }
listDetails.available[href].off = !this.checked;
updateWidgets();
};
/******************************************************************************/
var onListLinkClicked = function(ev) {
messaging.tell({
what: 'gotoExtensionURL',
url: 'asset-viewer.html?url=' + uDom(this).attr('href')
});
ev.preventDefault();
};
/******************************************************************************/
var onPurgeClicked = function() {
var button = uDom(this);
var li = button.parent();
var href = li.descendants('a').first().attr('href');
if ( !href ) {
return;
}
messaging.tell({ what: 'purgeCache', path: href });
button.remove();
if ( li.descendants('input').first().prop('checked') ) {
cacheWasPurged = true;
updateWidgets();
}
};
/******************************************************************************/
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 // Reload blacklists
var switches = []; var switches = [];
var lis = $('#blacklists .blacklistDetails');
var lis = uDom('#lists .listDetails');
var i = lis.length; var i = lis.length;
var path; var path;
while ( i-- ) { while ( i-- ) {
path = $(lis[i]).children('a').attr('href');
path = lis
.subset(i)
.descendants('a')
.attr('href');
switches.push({ switches.push({
location: path, location: path,
off: $(lis[i]).children('input').prop('checked') === false
off: lis.subset(i).descendants('input').prop('checked') === false
}); });
} }
messaging.tell({ messaging.tell({
what: 'reloadPresetBlacklists',
switches: switches
what: 'reloadHostsFiles',
switches: switches,
update: update
});
cacheWasPurged = false;
};
/******************************************************************************/
var buttonApplyHandler = function() {
reloadAll(false);
uDom('#buttonApply').toggleClass('enabled', false);
};
/******************************************************************************/
var buttonUpdateHandler = function() {
if ( needUpdate ) {
reloadAll(true);
}
};
/******************************************************************************/
var buttonPurgeAllHandler = function() {
var onCompleted = function() {
renderBlacklists();
};
messaging.ask({ what: 'purgeAllCaches' }, onCompleted);
};
/******************************************************************************/
var autoUpdateCheckboxChanged = function() {
messaging.tell({
what: 'userSettings',
name: 'autoUpdate',
value: this.checked
}); });
$('#blacklistsApply').attr('disabled', true );
}
};
/******************************************************************************/ /******************************************************************************/
$(function() {
$('#blacklists').on('change', '.blacklistDetails', selectedBlacklistsChanged);
$('#blacklistsApply').on('click', blacklistsApplyHandler);
var renderExternalLists = function() {
var onReceived = function(details) {
uDom('#externalHostsFiles').val(details);
externalHostsFiles = details;
};
messaging.ask({ what: 'userSettings', name: 'externalHostsFiles' }, onReceived);
};
/******************************************************************************/
var externalListsChangeHandler = function() {
uDom('#externalListsParse').prop(
'disabled',
this.value.trim() === externalHostsFiles
);
};
/******************************************************************************/
var externalListsApplyHandler = function() {
externalHostsFiles = uDom('#externalHostsFiles').val();
messaging.tell({
what: 'userSettings',
name: 'externalHostsFiles',
value: externalHostsFiles
});
renderBlacklists();
uDom('#externalListsParse').prop('disabled', true);
};
/******************************************************************************/
uDom.onLoad(function() {
uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
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('click', 'span.purge', onPurgeClicked);
uDom('#externalHostsFiles').on('input', externalListsChangeHandler);
uDom('#externalListsParse').on('click', externalListsApplyHandler);
renderBlacklists(); renderBlacklists();
renderExternalLists();
}); });
/******************************************************************************/ /******************************************************************************/

2
src/js/httpsb.js

@ -31,7 +31,7 @@
µm.pMatrix.setSwitch('chrome-scheme', false); µm.pMatrix.setSwitch('chrome-scheme', false);
µm.pMatrix.setSwitch(µm.behindTheSceneScope, false); µm.pMatrix.setSwitch(µm.behindTheSceneScope, false);
µm.pMatrix.setSwitch('opera-scheme', false); µm.pMatrix.setSwitch('opera-scheme', false);
µm.pMatrix.setCell('*', '*', '*', µm.Matrix.Green);
µm.pMatrix.setCell('*', '*', '*', µm.Matrix.Red);
µm.pMatrix.setCell('*', '*', 'css', µm.Matrix.Green); µm.pMatrix.setCell('*', '*', 'css', µm.Matrix.Green);
µm.pMatrix.setCell('*', '*', 'image', µm.Matrix.Green); µm.pMatrix.setCell('*', '*', 'image', µm.Matrix.Green);
µm.pMatrix.setCell('*', '*', 'frame', µm.Matrix.Red); µm.pMatrix.setCell('*', '*', 'frame', µm.Matrix.Red);

3
src/js/liquid-dict.js

@ -28,6 +28,7 @@
var LiquidDict = function() { var LiquidDict = function() {
this.dict = {}; this.dict = {};
this.count = 0; this.count = 0;
this.duplicateCount = 0;
this.bucketCount = 0; this.bucketCount = 0;
this.frozenBucketCount = 0; this.frozenBucketCount = 0;
@ -160,6 +161,7 @@ LiquidDict.prototype.add = function(word) {
this.count += 1; this.count += 1;
return true; return true;
} }
this.duplicateCount += 1;
return false; return false;
}; };
@ -181,6 +183,7 @@ LiquidDict.prototype.freeze = function() {
LiquidDict.prototype.reset = function() { LiquidDict.prototype.reset = function() {
this.dict = {}; this.dict = {};
this.count = 0; this.count = 0;
this.duplicateCount = 0;
this.bucketCount = 0; this.bucketCount = 0;
this.frozenBucketCount = 0; this.frozenBucketCount = 0;
}; };

49
src/js/messaging-handlers.js

@ -474,15 +474,46 @@ var onMessage = function(request, sender, callback) {
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
// ubiquitous-rules.js
// hosts-files.js
(function() { (function() {
var µm = µMatrix;
/******************************************************************************/
var getLists = function(callback) {
var r = {
available: null,
cache: null,
current: µm.liveHostsFiles,
blockedHostnameCount: µm.ubiquitousBlacklist.count,
autoUpdate: µm.userSettings.autoUpdate
};
var onMetadataReady = function(entries) {
r.cache = entries;
callback(r);
};
var onAvailableHostsFilesReady = function(lists) {
r.available = lists;
µm.assets.metadata(onMetadataReady);
};
µm.getAvailableHostsFiles(onAvailableHostsFilesReady);
};
/******************************************************************************/
var onMessage = function(request, sender, callback) { var onMessage = function(request, sender, callback) {
var µm = µMatrix; var µm = µMatrix;
// Async // Async
switch ( request.what ) { switch ( request.what ) {
case 'getLists':
return getLists(callback);
case 'purgeAllCaches':
return µm.assets.purgeAll(callback);
default: default:
break; break;
} }
@ -491,6 +522,10 @@ var onMessage = function(request, sender, callback) {
var response; var response;
switch ( request.what ) { switch ( request.what ) {
case 'purgeCache':
µm.assets.purge(request.path);
break;
default: default:
return µm.messaging.defaultHandler(request, sender, callback); return µm.messaging.defaultHandler(request, sender, callback);
} }
@ -498,7 +533,7 @@ var onMessage = function(request, sender, callback) {
callback(response); callback(response);
}; };
µMatrix.messaging.listen('ubiquitous-rules.js', onMessage);
µMatrix.messaging.listen('hosts-files.js', onMessage);
})(); })();
@ -600,12 +635,6 @@ var onMessage = function(request, sender, callback) {
// Async // Async
switch ( request.what ) { switch ( request.what ) {
case 'getAssetUpdaterList':
return µm.assetUpdater.getList(callback);
case 'launchAssetUpdater':
return µm.assetUpdater.update(request.list, callback);
case 'readUserSettings': case 'readUserSettings':
return chrome.storage.local.get(µm.userSettings, callback); return chrome.storage.local.get(µm.userSettings, callback);
@ -617,10 +646,6 @@ var onMessage = function(request, sender, callback) {
var response; var response;
switch ( request.what ) { switch ( request.what ) {
case 'loadUpdatableAssets':
response = µm.loadUpdatableAssets();
break;
case 'getSomeStats': case 'getSomeStats':
response = { response = {
storageQuota: µm.storageQuota, storageQuota: µm.storageQuota,

8
src/js/messaging.js

@ -138,8 +138,8 @@ var onMessage = function(request, port) {
function defaultHandler(request, sender, callback) { function defaultHandler(request, sender, callback) {
// Async // Async
switch ( request.what ) { switch ( request.what ) {
case 'loadUbiquitousAllowRules':
return µm.loadUbiquitousWhitelists();
case 'getAssetContent':
return µm.assets.getLocal(request.url, callback);
default: default:
break; break;
@ -165,8 +165,8 @@ function defaultHandler(request, sender, callback) {
µm.utils.gotoURL(request); µm.utils.gotoURL(request);
break; break;
case 'reloadPresetBlacklists':
µm.reloadPresetBlacklists(request.switches);
case 'reloadHostsFiles':
µm.reloadHostsFiles(request.switches);
break; break;
case 'userSettings': case 'userSettings':

269
src/js/storage.js

@ -91,134 +91,181 @@
/******************************************************************************/ /******************************************************************************/
µMatrix.loadUbiquitousBlacklists = function() {
var µm = µMatrix;
var blacklists;
var blacklistLoadCount;
var obsoleteBlacklists = [];
µMatrix.getAvailableHostsFiles = function(callback) {
var availableHostsFiles = {};
var redirections = {};
var µm = this;
var removeObsoleteBlacklistsHandler = function(store) {
if ( !store.remoteBlacklists ) {
return;
// selected lists
var onSelectedHostsFilesLoaded = function(store) {
var lists = store.liveHostsFiles;
var locations = Object.keys(lists);
var oldLocation, newLocation;
var availableEntry, storedEntry;
while ( oldLocation = locations.pop() ) {
newLocation = redirections[oldLocation] || oldLocation;
availableEntry = availableHostsFiles[newLocation];
if ( availableEntry === undefined ) {
continue;
} }
var location;
while ( location = obsoleteBlacklists.pop() ) {
delete store.remoteBlacklists[location];
storedEntry = lists[oldLocation];
availableEntry.off = storedEntry.off || false;
µm.assets.setHomeURL(newLocation, availableEntry.homeURL);
if ( storedEntry.entryCount !== undefined ) {
availableEntry.entryCount = storedEntry.entryCount;
} }
chrome.storage.local.set(store);
if ( storedEntry.entryUsedCount !== undefined ) {
availableEntry.entryUsedCount = storedEntry.entryUsedCount;
}
// This may happen if the list name was pulled from the list content
if ( availableEntry.title === '' && storedEntry.title !== '' ) {
availableEntry.title = storedEntry.title;
}
}
callback(availableHostsFiles);
}; };
var removeObsoleteBlacklists = function() {
if ( obsoleteBlacklists.length === 0 ) {
return;
// built-in lists
var onBuiltinHostsFilesLoaded = function(details) {
var location, locations;
try {
locations = JSON.parse(details.content);
} catch (e) {
locations = {};
} }
var hostsFileEntry;
for ( location in locations ) {
if ( locations.hasOwnProperty(location) === false ) {
continue;
}
hostsFileEntry = locations[location];
availableHostsFiles['assets/thirdparties/' + location] = hostsFileEntry;
if ( hostsFileEntry.old !== undefined ) {
redirections[hostsFileEntry.old] = location;
delete hostsFileEntry.old;
}
}
// Now get user's selection of lists
chrome.storage.local.get( chrome.storage.local.get(
{ 'remoteBlacklists': µm.remoteBlacklists },
removeObsoleteBlacklistsHandler
{ 'liveHostsFiles': availableHostsFiles },
onSelectedHostsFilesLoaded
); );
}; };
var mergeBlacklist = function(details) {
µm.mergeUbiquitousBlacklist(details);
blacklistLoadCount -= 1;
if ( blacklistLoadCount === 0 ) {
loadBlacklistsEnd();
// permanent hosts files
var location;
var lists = this.permanentHostsFiles;
for ( location in lists ) {
if ( lists.hasOwnProperty(location) === false ) {
continue;
} }
availableHostsFiles[location] = lists[location];
}
// custom lists
var c;
var locations = this.userSettings.externalHostsFiles.split('\n');
for ( var i = 0; i < locations.length; i++ ) {
location = locations[i].trim();
c = location.charAt(0);
if ( location === '' || c === '!' || c === '#' ) {
continue;
}
// Coarse validation
if ( /[^0-9A-Za-z!*'();:@&=+$,\/?%#\[\]_.~-]/.test(location) ) {
continue;
}
availableHostsFiles[location] = {
title: '',
external: true
}; };
}
// get built-in block lists.
this.assets.get('assets/umatrix/hosts-files.json', onBuiltinHostsFilesLoaded);
};
/******************************************************************************/
µMatrix.loadHostsFiles = function(callback) {
var µm = µMatrix;
var hostsFileLoadCount;
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
}
var loadBlacklistsEnd = function() {
var loadHostsFilesEnd = function() {
µm.ubiquitousBlacklist.freeze(); µm.ubiquitousBlacklist.freeze();
removeObsoleteBlacklists();
µm.messaging.announce({ what: 'loadUbiquitousBlacklistCompleted' });
chrome.storage.local.set({ 'liveHostsFiles': µm.liveHostsFiles });
µm.messaging.announce({ what: 'loadHostsFilesCompleted' });
callback();
}; };
var loadBlacklistsStart = function(store) {
// rhill 2013-12-10: set all existing entries to `false`.
µm.ubiquitousBlacklist.reset();
blacklists = store.remoteBlacklists;
var blacklistLocations = Object.keys(store.remoteBlacklists);
var mergeHostsFile = function(details) {
µm.mergeHostsFile(details);
hostsFileLoadCount -= 1;
if ( hostsFileLoadCount === 0 ) {
loadHostsFilesEnd();
}
};
blacklistLoadCount = blacklistLocations.length;
if ( blacklistLoadCount === 0 ) {
loadBlacklistsEnd();
var loadHostsFilesStart = function(hostsFiles) {
µm.liveHostsFiles = hostsFiles;
µm.ubiquitousBlacklist.reset();
var locations = Object.keys(hostsFiles);
hostsFileLoadCount = locations.length;
if ( hostsFileLoadCount === 0 ) {
loadHostsFilesEnd();
return; return;
} }
// Load each preset blacklist which is not disabled.
// Load all hosts file which are not disabled.
var location; var location;
while ( location = blacklistLocations.pop() ) {
// If loaded list location is not part of default list locations,
// remove its entry from local storage.
if ( !µm.remoteBlacklists[location] ) {
obsoleteBlacklists.push(location);
blacklistLoadCount -= 1;
while ( location = locations.pop() ) {
if ( hostsFiles[location].off ) {
hostsFileLoadCount -= 1;
continue; continue;
} }
// https://github.com/gorhill/httpswitchboard/issues/218
// Transfer potentially existing list title into restored list data.
if ( store.remoteBlacklists[location].title !== µm.remoteBlacklists[location].title ) {
store.remoteBlacklists[location].title = µm.remoteBlacklists[location].title;
}
// Store details of this preset blacklist
µm.remoteBlacklists[location] = store.remoteBlacklists[location];
// rhill 2013-12-09:
// Ignore list if disabled
// https://github.com/gorhill/httpswitchboard/issues/78
if ( store.remoteBlacklists[location].off ) {
blacklistLoadCount -= 1;
continue;
}
µm.assets.get(location, mergeBlacklist);
µm.assets.get(location, mergeHostsFile);
} }
}; };
var onListOfBlockListsLoaded = function(details) {
// Initialize built-in list of 3rd-party block lists.
var lists = JSON.parse(details.content);
for ( var location in lists ) {
if ( lists.hasOwnProperty(location) === false ) {
continue;
}
µm.remoteBlacklists['assets/thirdparties/' + location] = lists[location];
}
// Now get user's selection of list of block lists.
chrome.storage.local.get(
{ 'remoteBlacklists': µm.remoteBlacklists },
loadBlacklistsStart
);
};
this.getAvailableHostsFiles(loadHostsFilesStart);
};
// Reset list of 3rd-party block lists.
for ( var location in this.remoteBlacklists ) {
if ( location.indexOf('assets/thirdparties/') === 0 ) {
delete this.remoteBlacklists[location];
}
}
/******************************************************************************/
// Get new list of 3rd-party block lists.
this.assets.get('assets/umatrix/ubiquitous-block-lists.json', onListOfBlockListsLoaded);
µMatrix.mergeHostsFile = function(details) {
// 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;
this.mergeHostsFileContent(details.content);
usedCount = this.ubiquitousBlacklist.count - usedCount;
duplicateCount = this.ubiquitousBlacklist.duplicateCount - duplicateCount;
var hostsFilesMeta = this.liveHostsFiles[details.path];
hostsFilesMeta.entryCount = usedCount + duplicateCount;
hostsFilesMeta.entryUsedCount = usedCount;
}; };
/******************************************************************************/ /******************************************************************************/
µMatrix.mergeUbiquitousBlacklist = function(details) {
// console.log('storage.js > mergeUbiquitousBlacklist from "%s": "%s..."', details.path, details.content.slice(0, 40));
µMatrix.mergeHostsFileContent = function(rawText) {
// console.log('storage.js > mergeHostsFileContent from "%s": "%s..."', details.path, details.content.slice(0, 40));
var rawText = details.content;
var rawEnd = rawText.length; var rawEnd = rawText.length;
// rhill 2013-10-21: No need to prefix with '* ', the hostname is just what
// we need for preset blacklists. The prefix '* ' is ONLY needed when
// used as a filter in temporary blacklist.
var ubiquitousBlacklist = this.ubiquitousBlacklist; var ubiquitousBlacklist = this.ubiquitousBlacklist;
var thisListCount = 0;
var thisListUsedCount = 0;
var reLocalhost = /(^|\s)(localhost\.localdomain|localhost|local|broadcasthost|0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)(?=\s|$)/g; var reLocalhost = /(^|\s)(localhost\.localdomain|localhost|local|broadcasthost|0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)(?=\s|$)/g;
var reAsciiSegment = /^[\x21-\x7e]+$/; var reAsciiSegment = /^[\x21-\x7e]+$/;
var matches; var matches;
var lineBeg = 0, lineEnd; var lineBeg = 0, lineEnd;
var line, c;
var line;
while ( lineBeg < rawEnd ) { while ( lineBeg < rawEnd ) {
lineEnd = rawText.indexOf('\n', lineBeg); lineEnd = rawText.indexOf('\n', lineBeg);
@ -235,20 +282,10 @@
line = rawText.slice(lineBeg, lineEnd).trim(); line = rawText.slice(lineBeg, lineEnd).trim();
lineBeg = lineEnd + 1; lineBeg = lineEnd + 1;
// Strip comments
c = line.charAt(0);
if ( c === '!' || c === '[' ) {
continue;
}
if ( c === '#' ) {
continue;
}
// https://github.com/gorhill/httpswitchboard/issues/15 // https://github.com/gorhill/httpswitchboard/issues/15
// Ensure localhost et al. don't end up in the ubiquitous blacklist. // Ensure localhost et al. don't end up in the ubiquitous blacklist.
line = line line = line
.replace(/\s+#.*$/, '')
.replace(/#.*$/, '')
.toLowerCase() .toLowerCase()
.replace(reLocalhost, '') .replace(reLocalhost, '')
.trim(); .trim();
@ -273,16 +310,8 @@
continue; continue;
} }
thisListCount++;
if ( ubiquitousBlacklist.add(line) ) {
thisListUsedCount++;
ubiquitousBlacklist.add(line);
} }
}
// For convenience, store the number of entries for this
// blacklist, user might be happy to know this information.
this.remoteBlacklists[details.path].entryCount = thisListCount;
this.remoteBlacklists[details.path].entryUsedCount = thisListUsedCount;
}; };
/******************************************************************************/ /******************************************************************************/
@ -290,26 +319,23 @@
// `switches` contains the preset blacklists for which the switch must be // `switches` contains the preset blacklists for which the switch must be
// revisited. // revisited.
µMatrix.reloadPresetBlacklists = function(switches) {
var presetBlacklists = this.remoteBlacklists;
µMatrix.reloadHostsFiles = function(switches) {
var liveHostsFiles = this.liveHostsFiles;
// Toggle switches // Toggle switches
var i = switches.length; var i = switches.length;
while ( i-- ) { while ( i-- ) {
if ( !presetBlacklists[switches[i].location] ) {
if ( !liveHostsFiles[switches[i].location] ) {
continue; continue;
} }
presetBlacklists[switches[i].location].off = !!switches[i].off;
liveHostsFiles[switches[i].location].off = !!switches[i].off;
} }
// Save switch states // Save switch states
chrome.storage.local.set( chrome.storage.local.set(
{ 'remoteBlacklists': presetBlacklists },
this.getBytesInUse.bind(this)
{ 'liveHostsFiles': liveHostsFiles },
this.loadHostsFiles.bind(this)
); );
// Now force reload
this.loadUbiquitousBlacklists();
}; };
/******************************************************************************/ /******************************************************************************/
@ -333,7 +359,7 @@
// Load updatable assets // Load updatable assets
µMatrix.loadUpdatableAssets = function() { µMatrix.loadUpdatableAssets = function() {
this.loadUbiquitousBlacklists();
this.loadHostsFiles();
this.loadPublicSuffixList(); this.loadPublicSuffixList();
}; };
@ -342,12 +368,9 @@
// Load all // Load all
µMatrix.load = function() { µMatrix.load = function() {
// user
this.loadUserSettings(); this.loadUserSettings();
this.loadMatrix(); this.loadMatrix();
// load updatable assets -- after updating them if needed
this.assetUpdater.update(null, this.loadUpdatableAssets.bind(this));
this.loadUpdatableAssets();
this.getBytesInUse(); this.getBytesInUse();
}; };

2
src/js/traffic.js

@ -883,7 +883,7 @@ chrome.webRequest.onBeforeRequest.addListener(
[ "blocking" ] [ "blocking" ]
); );
console.log('HTTP Switchboard> Beginning to intercept net requests at %s', (new Date()).toISOString());
console.log('µMatrix > Beginning to intercept net requests at %s', (new Date()).toISOString());
chrome.webRequest.onBeforeSendHeaders.addListener( chrome.webRequest.onBeforeSendHeaders.addListener(
onBeforeSendHeadersHandler, onBeforeSendHeadersHandler,

30
src/js/udom.js

@ -166,6 +166,18 @@ var isDescendantOf = function(descendant, ancestor) {
/******************************************************************************/ /******************************************************************************/
var nodeInNodeList = function(node, nodeList) {
var i = nodeList.length;
while ( i-- ) {
if ( nodeList[i] === node ) {
return true;
}
}
return false;
};
/******************************************************************************/
var doesMatchSelector = function(node, selector) { var doesMatchSelector = function(node, selector) {
if ( !node ) { if ( !node ) {
return false; return false;
@ -599,8 +611,13 @@ DOMList.prototype.toggleClass = function(className, targetState) {
var makeEventHandler = function(selector, callback) { var makeEventHandler = function(selector, callback) {
return function(event) { return function(event) {
if ( doesMatchSelector(event.target, selector) ) {
callback.call(event.target, event);
var dispatcher = event.currentTarget;
if ( !dispatcher || typeof dispatcher.querySelectorAll !== 'function' ) {
return;
}
var receiver = event.target;
if ( nodeInNodeList(receiver, dispatcher.querySelectorAll(selector)) ) {
callback.call(receiver, event);
} }
}; };
}; };
@ -609,14 +626,13 @@ DOMList.prototype.on = function(etype, selector, callback) {
if ( typeof selector === 'function' ) { if ( typeof selector === 'function' ) {
callback = selector; callback = selector;
selector = undefined; selector = undefined;
} else {
callback = makeEventHandler(selector, callback);
} }
var i = this.nodes.length; var i = this.nodes.length;
while ( i-- ) { while ( i-- ) {
if ( selector !== undefined ) {
this.nodes[i].addEventListener(etype, makeEventHandler(selector, callback), true);
} else {
this.nodes[i].addEventListener(etype, callback);
}
this.nodes[i].addEventListener(etype, callback, selector !== undefined);
} }
return this; return this;
}; };

8
tools/_locales/de/messages.json

@ -409,14 +409,6 @@
}, },
"ubiquitousWhatIsThisHeader" : {
"message": "Was ist das?",
"description": "English: What is this?"
},
"ubiquitousWhatIsThisPrompt" : {
"message": "&ldquo;Omnipräsente Regeln&rdquo; sind Regeln, die überall gelten, d.h. in allen Geltungsbereichen.",
"description": "English: &ldquo;Ubiquitous rules&rdquo; are rules which applies everywhere, i.e. in all scopes."
},
"ubiquitousListsOfBlockedHostsPrompt1" : { "ubiquitousListsOfBlockedHostsPrompt1" : {
"message": "Alle Listen blockierter Hostnamen werden als omnipräsente Regeln geladen, womit diese Hostnamen in allen Geltungsbereichen auf der Blacklist stehen.", "message": "Alle Listen blockierter Hostnamen werden als omnipräsente Regeln geladen, womit diese Hostnamen in allen Geltungsbereichen auf der Blacklist stehen.",
"description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes." "description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes."

76
tools/_locales/en/messages.json

@ -24,7 +24,7 @@
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
}, },
"ubiquitousRulesPageName" : { "ubiquitousRulesPageName" : {
"message": "Ubiquitous rules",
"message": "Hosts files",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
}, },
"aboutPageName": { "aboutPageName": {
@ -409,57 +409,53 @@
}, },
"ubiquitousWhatIsThisHeader" : {
"message": "What is this?",
"description": "English: What is this?"
"hostsFilesPrompt" : {
"message": "All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.",
"description": "English: All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope."
}, },
"ubiquitousWhatIsThisPrompt" : {
"message": "&ldquo;Ubiquitous rules&rdquo; are rules which apply everywhere, i.e. in all scopes. A ubiquitous rule can be overridden by any scoped rule for the same element.",
"description": "English: &ldquo;Ubiquitous rules&rdquo; are rules which apply everywhere, i.e. in all scopes. A ubiquitous rule can be overridden by any scoped rule for the same element."
"hostsFilesStats" : {
"message": "{{blockedHostnameCount}} distinct blocked hostnames from:",
"description": "English: {{blockedHostnameCount}} distinct blocked hostnames from:"
}, },
"ubiquitousListsOfBlockedHostsPrompt1" : {
"message": "All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes.",
"description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes."
},
"ubiquitousListsOfBlockedHostsPrompt2" : {
"message": "{{ubiquitousBlacklistCount}} distinct blocked hostnames from:",
"description": "English: {{ubiquitousBlacklistCount}} distinct blocked hostnames from:"
},
"ubiquitousListsOfBlockedHostsPerListStats" : {
"hostsFilesPerFileStats" : {
"message": "{{used}} used out of {{total}}", "message": "{{used}} used out of {{total}}",
"description": "English: {{used}} used out of {{total}}" "description": "English: {{used}} used out of {{total}}"
}, },
"ubiquitousListsOfBlockedHostsHeader" : {
"message": "Lists of blocked hosts",
"description": "English: Lists of blocked hosts"
"hostsFilesApplyChanges" : {
"message": "Apply changes",
"description": "English: Apply changes"
}, },
"userUbiquitousBlacklistHeader" : {
"message": "Your block rules",
"description": "English: Your block rules"
"hostsFilesAutoUpdatePrompt":{
"message":"Auto-update filter lists.",
"description":"English: Auto-update filter lists."
}, },
"userUbiquitousWhitelistHeader" : {
"message": "Your allow rules",
"description": "English: Your allow rules"
"hostsFilesUpdateNow":{
"message":"Update now",
"description":"English: Update now"
}, },
"ubiquitousApplyChanges" : {
"message": "Apply changes",
"description": "English: Apply changes"
"hostsFilesPurgeAll":{
"message":"Purge all caches",
"description":"English: Purge all caches"
},
"hostsFilesExternalListsHint":{
"message":"One URL per line. Lines prefixed with &lsquo;!&rsquo; will be ignored. Invalid URLs will be silently ignored.",
"description":"English: One URL per line. Lines prefixed with &lsquo;!&rsquo; will be ignored. Invalid URLs will be silently ignored."
}, },
"ubiquitousFormatHint" : {
"message": "One rule per line. A rule can be a plain hostname, or an <a href='https://adblockplus.org/en/filters'>Adblock Plus-compatible filter</a>. Lines prefixed with &lsquo;#&rsquo; will be ignored.",
"description": "English: One rule per line. A rule can be a plain hostname, or an Adblock Plus-compatible filter. Lines prefixed with &lsquo;#&rsquo; will be ignored."
"hostsFilesExternalListsParse":{
"message":"Parse",
"description":"English: Parse"
}, },
"ubiquitousAllowFormatHint" : {
"message": "One rule per line. A rule can be a plain hostname, or an Adblock Plus-compatible exception filter (prefixed with &lsquo;@@&rsquo;). Lines prefixed with &lsquo;#&rsquo; will be ignored.",
"description": "English: One rule per line. A rule can be a plain hostname, or an Adblock Plus-compatible exception filter (prefixed with &lsquo;@@&rsquo;). Lines prefixed with &lsquo;#&rsquo; will be ignored."
"hostsFilesExternalListPurge":{
"message":"purge cache",
"description":"English: purge cache"
}, },
"ubiquitousImport" : {
"message": "Import and append",
"description": "English: Import and append"
"hostsFilesExternalListNew":{
"message":"new version available",
"description":"English: new version available"
}, },
"ubiquitousExport" : {
"message": "Export",
"description": "English: Export"
"hostsFilesExternalListObsolete":{
"message":"outdated",
"description":"English: outdated"
}, },

8
tools/_locales/fr/messages.json

@ -409,14 +409,6 @@
}, },
"ubiquitousWhatIsThisHeader" : {
"message": "De quoi s'agit-il ?",
"description": "English: What is this?"
},
"ubiquitousWhatIsThisPrompt" : {
"message": "Les &ldquo;règles à portée universelle&rdquo; sont des règles qui s'appliquent dans TOUS les contextes.",
"description": "English: &ldquo;Ubiquitous rules&rdquo; are rules which applies everywhere, i.e. in all scopes."
},
"ubiquitousListsOfBlockedHostsPrompt1" : { "ubiquitousListsOfBlockedHostsPrompt1" : {
"message": "Tous les hôtes des listes prédéfinies sont traités comme étant des règles universelles. Elles sont intégrées au sein de l'extension et peuvent être mises à jour dans l'onglet À propos.", "message": "Tous les hôtes des listes prédéfinies sont traités comme étant des règles universelles. Elles sont intégrées au sein de l'extension et peuvent être mises à jour dans l'onglet À propos.",
"description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes." "description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes."

8
tools/_locales/ru/messages.json

@ -409,14 +409,6 @@
}, },
"ubiquitousWhatIsThisHeader" : {
"message": "Что это?",
"description": "English: What is this?"
},
"ubiquitousWhatIsThisPrompt" : {
"message": "&ldquo;Глобальные правила&rdquo; правила, применяемые везде, во всех областях.",
"description": "English: &ldquo;Ubiquitous rules&rdquo; are rules which applies everywhere, i.e. in all scopes."
},
"ubiquitousListsOfBlockedHostsPrompt1" : { "ubiquitousListsOfBlockedHostsPrompt1" : {
"message": "Все списки заблокированных хостов загружаются, как глобальные правила, в связи с чем их владельцы находятся в черном списке для всех областей.", "message": "Все списки заблокированных хостов загружаются, как глобальные правила, в связи с чем их владельцы находятся в черном списке для всех областей.",
"description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes." "description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes."

8
tools/_locales/zh_CN/messages.json

@ -409,14 +409,6 @@
}, },
"ubiquitousWhatIsThisHeader" : {
"message": "这是什么?",
"description": "English: What is this?"
},
"ubiquitousWhatIsThisPrompt" : {
"message": "“普适规则”是应用到所有地方,即所有作用域,的规则。一条普适规则可以被任何针对同一元素的作用域规则所覆盖。",
"description": "English: &ldquo;Ubiquitous rules&rdquo; are rules which apply everywhere, i.e. in all scopes. A ubiquitous rule can be overridden by any scoped rule for the same element."
},
"ubiquitousListsOfBlockedHostsPrompt1" : { "ubiquitousListsOfBlockedHostsPrompt1" : {
"message": "所有屏蔽站点的列表都作为普适规则被加载,所以这些站点将在所有作用域的黑名单中。", "message": "所有屏蔽站点的列表都作为普适规则被加载,所以这些站点将在所有作用域的黑名单中。",
"description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes." "description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes."

Loading…
Cancel
Save