diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 629d446..601afa7 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -289,7 +289,7 @@ "description": "" }, "settingsStrictBlockingInfo": { - "message": "

Striktes\nBlockieren,\neingeführt in version 0.3.6,\nbedeutet: Selbst\nwenn du einen spezifischen Hostnamen auf die Whitelist setzt, bleiben die Arten von Anfragen, die standardmäßig blockiert sind (Plugins,\nFrames, etc.), weiterhin auf der Blacklist. Für einige Benutzer ist das möglicherweise\nzu lästig, daher dieser Schalter.

\n

Striktes Blockieren an: blockierte Arten von Anfragen (falls vorhanden)\nfür einen Hostnamen auf der Whitelist bleiben weiterhin auf der Blacklist (sofern du diese Arten von Anfragen für diesen Hostnamen nicht ausdrücklich erlaubst.)

\n

Striktes Blockieren aus: blockierte Arten von Anfragen (falls vorhanden)\nfür einen Hostnamen auf der Whitelist sind erlaubt (sofern du diese Arten von Anfragen für diesen Hostnamen nicht ausdrücklich verbietest.)

", + "message": "

Striktes\nBlockieren,\neingeführt in version 0.3.6,\nbedeutet: Selbst\nwenn du einen spezifischen Hostnamen auf die Whitelist setzt, bleiben die Arten von Anfragen, die standardmäßig blockiert sind (Plugins,\nFrames, etc.), weiterhin auf der Blacklist. Für einige Benutzer ist das möglicherweise\nzu lästig, daher dieser Schalter.

\n

Striktes Blockieren an: blockierte Arten von Anfragen (falls vorhanden)\nfür einen Hostnamen auf der Whitelist bleiben weiterhin auf der Blacklist (sofern du diese Arten von Anfragen für diesen Hostnamen nicht ausdrücklich erlaubst.)

\n

Striktes Blockieren aus: blockierte Arten von Anfragen (falls vorhanden)\nfür einen Hostnamen auf der Whitelist sind erlaubt (sofern du diese Arten von Anfragen für diesen Hostnamen nicht ausdrücklich verbietest.)

", "description": "To help user understand the purpose of strict blocking" }, "settingsAutoCreateSiteScopePrompt": { @@ -386,13 +386,61 @@ }, + "ubiquitousWhatIsThisHeader" : { + "message": "What is this?", + "description": "English: What is this?" + }, + "ubiquitousWhatIsThisPrompt" : { + "message": "“Ubiquitous rules” are rules which applies everywhere, i.e. in all scopes.", + "description": "English: “Ubiquitous rules” are rules which applies everywhere, i.e. in all scopes." + }, + "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:" + }, + "ubiquitousParseAllABPFiltersPrompt1" : { + "message": "Parse and enforce Adblock+ complex filters (beta).", + "description": "English: Parse and enforce Adblock+ complex filters (beta)." + }, + "ubiquitousParseAllABPFiltersPrompt2" : { + "message": "{{abpFilterCount}} complex filters used.", + "description": "English: Parse and enforce Adblock+ complex filters (beta)." + }, + "ubiquitousParseAllABPFiltersInfo" : { + "message": "

Normally, HTTP Switchboard will extract from Adblock Plus-compatible filter lists only those filters which are used to block an entire domain (example: “||a.ucoz.net^”).

This option enables the parsing and enforcing of finer-grained Adblock Plus filters (example: “/adwords-conversion-tracking.”). These complex filters are incompatible with the matrix, and thus are only used internally to evaluate whether a net request should be blocked: they have no user interface counterpart.

Even if this option is enabled, Adblock Plus filters used for element hiding are completely ignored.

Memory usage requirement is higher when this option is enabled.

", + "description": "English: ..." + }, "ubiquitousListsOfBlockedHostsHeader" : { "message": "Lists of blocked hosts", - "description": "header of ubiquitous lists of blocked hosts section" + "description": "English: Lists of blocked hosts" + }, + "userUbiquitousBlacklistHeader" : { + "message": "Your blacklisted hosts", + "description": "English: Your blacklisted hosts" + }, + "userUbiquitousWhitelistHeader" : { + "message": "Your whitelisted hosts", + "description": "English: Your whitelisted hosts" + }, + "ubiquitousApplyChanges" : { + "message": "Apply changes", + "description": "English: Apply changes" + }, + "ubiquitousFormatHint" : { + "message": "One hostname per line. Lines prefixed with ‘#’ will be ignored.", + "description": "English: One hostname per line. Line prefixed with ‘#’ will be ignored." + }, + "ubiquitousImport" : { + "message": "Import and append", + "description": "English: Import and append" }, - "ubiquitousUserBlockedHostsHeader" : { - "message": "Your list of blocked hosts", - "description": "header of user's list of blocked hosts section" + "ubiquitousExport" : { + "message": "Export", + "description": "English: Export" }, diff --git a/_locales/en/messages.json b/_locales/en/messages.json index cbef820..8d5abd1 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -289,7 +289,7 @@ "description": "" }, "settingsStrictBlockingInfo": { - "message": "

Strict\nblocking,\nintroduced in version 0.3.6,\nmeans that even\nif you whitelist a specific hostname, blacklisted type of requests (plugins,\nframes, etc.) will remain blacklisted. For some users this maybe\ntoo bothersome, hence this switch.

\n

Strict blocking on: blacklisted types of request (if any)\nfor a whitelisted hostname are blocked (unless you explicitly whitelist\nspecifically these types of request for the whitelisted hostname.)

\n

Strict blocking off: blacklisted types of request (if any)\nfor a whitelisted hostname are allowed (unless you explicitly blacklist\nspecifically these types of request for the whitelisted hostname.)

", + "message": "

Strict\nblocking,\nintroduced in version 0.3.6,\nmeans that even\nif you whitelist a specific hostname, blacklisted type of requests (plugins,\nframes, etc.) will remain blacklisted. For some users this maybe\ntoo bothersome, hence this switch.

\n

Strict blocking on: blacklisted types of request (if any)\nfor a whitelisted hostname are blocked (unless you explicitly whitelist\nspecifically these types of request for the whitelisted hostname.)

\n

Strict blocking off: blacklisted types of request (if any)\nfor a whitelisted hostname are allowed (unless you explicitly blacklist\nspecifically these types of request for the whitelisted hostname.)

", "description": "To help user understand the purpose of strict blocking" }, "settingsAutoCreateSiteScopePrompt": { @@ -382,13 +382,61 @@ }, + "ubiquitousWhatIsThisHeader" : { + "message": "What is this?", + "description": "English: What is this?" + }, + "ubiquitousWhatIsThisPrompt" : { + "message": "“Ubiquitous rules” are rules which applies everywhere, i.e. in all scopes.", + "description": "English: “Ubiquitous rules” are rules which applies everywhere, i.e. in all scopes." + }, + "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:" + }, + "ubiquitousParseAllABPFiltersPrompt1" : { + "message": "Parse and enforce Adblock+ complex filters (beta).", + "description": "English: Parse and enforce Adblock+ complex filters (beta)." + }, + "ubiquitousParseAllABPFiltersPrompt2" : { + "message": "{{abpFilterCount}} complex filters used.", + "description": "English: Parse and enforce Adblock+ complex filters (beta)." + }, + "ubiquitousParseAllABPFiltersInfo" : { + "message": "

Normally, HTTP Switchboard will extract from Adblock Plus-compatible filter lists only those filters which are used to block an entire domain (example: “||a.ucoz.net^”).

This option enables the parsing and enforcing of finer-grained Adblock Plus filters (example: “/adwords-conversion-tracking.”). These complex filters are incompatible with the matrix, and thus are only used internally to evaluate whether a net request should be blocked: they have no user interface counterpart.

Even if this option is enabled, Adblock Plus filters used for element hiding are completely ignored.

Memory usage requirement is higher when this option is enabled.

", + "description": "English: ..." + }, "ubiquitousListsOfBlockedHostsHeader" : { "message": "Lists of blocked hosts", - "description": "header of ubiquitous lists of blocked hosts section" + "description": "English: Lists of blocked hosts" + }, + "userUbiquitousBlacklistHeader" : { + "message": "Your blacklisted hosts", + "description": "English: Your blacklisted hosts" + }, + "userUbiquitousWhitelistHeader" : { + "message": "Your whitelisted hosts", + "description": "English: Your whitelisted hosts" + }, + "ubiquitousApplyChanges" : { + "message": "Apply changes", + "description": "English: Apply changes" + }, + "ubiquitousFormatHint" : { + "message": "One hostname per line. Lines prefixed with ‘#’ will be ignored.", + "description": "English: One hostname per line. Line prefixed with ‘#’ will be ignored." + }, + "ubiquitousImport" : { + "message": "Import and append", + "description": "English: Import and append" }, - "ubiquitousUserBlockedHostsHeader" : { - "message": "Your list of blocked hosts", - "description": "header of user's list of blocked hosts section" + "ubiquitousExport" : { + "message": "Export", + "description": "English: Export" }, diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index 51d2bf0..76a4d5e 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -289,7 +289,7 @@ "description": "" }, "settingsStrictBlockingInfo": { - "message": "Introduit dans la version 0.3.6, le Blocage strict signifie que même si vous allouez un nom d'hôte spécifique, une requête restera bloquée si le type d'objet en question (plugins, iframes, etc) est lui-même bloqué; en d'autres termes, le blocage du type d'objet prime sur le domaine concerné. Le choix vous est proposé de l'activer ou non, car cela peut se montrer trop restrictif aux yeux de certains utilisateurs. Si l'option est activée, la requête est autorisée à condition que les nom d'hôte ET le type de requête soient autorisés. Si l'option n'est pas cochée, le nom d'hôte alloué suffit à autoriser la requête.", + "message": "Introduit dans la version 0.3.6, le Blocage strict signifie que même si vous allouez un nom d'hôte spécifique, une requête restera bloquée si le type d'objet en question (plugins, iframes, etc) est lui-même bloqué; en d'autres termes, le blocage du type d'objet prime sur le domaine concerné. Le choix vous est proposé de l'activer ou non, car cela peut se montrer trop restrictif aux yeux de certains utilisateurs. Si l'option est activée, la requête est autorisée à condition que les nom d'hôte ET le type de requête soient autorisés. Si l'option n'est pas cochée, le nom d'hôte alloué suffit à autoriser la requête.", "description": "To help user understand the purpose of strict blocking" }, "settingsAutoCreateSiteScopePrompt": { @@ -382,13 +382,61 @@ }, + "ubiquitousWhatIsThisHeader" : { + "message": "De quoi s'agit-il?", + "description": "English: What is this?" + }, + "ubiquitousWhatIsThisPrompt" : { + "message": "Les “règles universelles” sont des règles qui s'appliquent dans tous les contextes.", + "description": "English: “Ubiquitous rules” are rules which applies everywhere, i.e. in all scopes." + }, + "ubiquitousListsOfBlockedHostsPrompt1" : { + "message": "Tous les hôtes des listes prédéfinies sont traités comme étant des règles universelles.", + "description": "English: All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes." + }, + "ubiquitousListsOfBlockedHostsPrompt2" : { + "message": "{{ubiquitousBlacklistCount}} hôtes à bloquer:", + "description": "English: {{ubiquitousBlacklistCount}} distinct blocked hostnames from:" + }, + "ubiquitousParseAllABPFiltersPrompt1" : { + "message": "Lire et appliquer les filtres ABP complexes (beta).", + "description": "English: Parse and enforce Adblock+ complex filters (beta)." + }, + "ubiquitousParseAllABPFiltersPrompt2" : { + "message": "{{abpFilterCount}} filtres ABP complexes utilisés.", + "description": "English: Parse and enforce Adblock+ complex filters (beta)." + }, + "ubiquitousParseAllABPFiltersInfo" : { + "message": "

Normally, HTTP Switchboard will extract from Adblock Plus-compatible filter lists only those filters which are used to block an entire domain (example: “||a.ucoz.net^”).

This option enables the parsing and enforcing of finer-grained Adblock Plus filters (example: “/adwords-conversion-tracking.”). These complex filters are incompatible with the matrix, and thus are only used internally to evaluate whether a net request should be blocked: they have no user interface counterpart.

Even if this option is enabled, Adblock Plus filters used for element hiding are completely ignored.

Memory usage requirement is higher when this option is enabled.

", + "description": "English: ..." + }, "ubiquitousListsOfBlockedHostsHeader" : { "message": "Listes d'hôtes à bloquer", - "description": "header of ubiquitous lists of blocked hosts section" + "description": "English: Lists of blocked hosts" }, - "ubiquitousUserBlockedHostsHeader" : { + "userUbiquitousBlacklistHeader" : { "message": "Votre liste d'hôtes à bloquer", - "description": "header of user's list of blocked hosts section" + "description": "English: Your blacklisted hosts" + }, + "userUbiquitousWhitelistHeader" : { + "message": "Votre liste d'hôtes à allouer", + "description": "English: Your whitelisted hosts" + }, + "ubiquitousApplyChanges" : { + "message": "Appliquer", + "description": "English: Apply changes" + }, + "ubiquitousFormatHint" : { + "message": "Un nom d'hôte par ligne. Les lignes débutant par ‘#’ seront ignorées.", + "description": "English: One hostname per line. Line prefixed with ‘#’ will be ignored." + }, + "ubiquitousImport" : { + "message": "Importer", + "description": "English: Import and append" + }, + "ubiquitousExport" : { + "message": "Exporter", + "description": "English: Export" }, diff --git a/js/async.js b/js/async.js index c3a63d4..c66af46 100644 --- a/js/async.js +++ b/js/async.js @@ -232,14 +232,6 @@ function onMessageHandler(request, sender, callback) { HTTPSB.onLocalAssetUpdated(request); break; - case 'localRemoveRemoteBlacklist': - localRemoveRemoteBlacklist(request.location); - break; - - case 'mergeBlacklistedHosts': - mergeBlacklistedHosts(request); - break; - case 'reloadPresetBlacklists': reloadPresetBlacklists(request.switches); break; diff --git a/js/background.js b/js/background.js index e7f8c7b..33fbc3d 100644 --- a/js/background.js +++ b/js/background.js @@ -123,9 +123,10 @@ var HTTPSB = { permanentScopes: null, factoryScope: null, - // Current entries from remote blacklists -- + // Current entries from ubiquitous lists -- // just hostnames, '*/' is implied, this saves significantly on memory. ubiquitousBlacklist: null, + ubiquitousWhitelist: null, // various stats requestStats: new WebRequestStats(), diff --git a/js/httpsb.js b/js/httpsb.js index 2822402..483ae9d 100644 --- a/js/httpsb.js +++ b/js/httpsb.js @@ -555,8 +555,13 @@ HTTPSB.getPermanentColor = function(scopeKey, type, hostname) { return 'xxx'; } } - if ( type === '*' && this.ubiquitousBlacklist.test(hostname) ) { - return 'rdp'; + if ( type === '*' ) { + if ( this.ubiquitousWhitelist.test(hostname) ) { + return 'gdp'; + } + if ( this.ubiquitousBlacklist.test(hostname) ) { + return 'rdp'; + } } return 'xxx'; }; diff --git a/js/liquid-dict.js b/js/liquid-dict.js index 193231f..55b8130 100644 --- a/js/liquid-dict.js +++ b/js/liquid-dict.js @@ -172,5 +172,5 @@ HTTPSB.LiquidDict.prototype.reset = function() { /******************************************************************************/ HTTPSB.ubiquitousBlacklist = new HTTPSB.LiquidDict(); -// HTTPSB.ubiquitousWhitelist = new HTTPSB.LiquidDict(); +HTTPSB.ubiquitousWhitelist = new HTTPSB.LiquidDict(); diff --git a/js/lists.js b/js/lists.js index 6432198..c923dce 100644 --- a/js/lists.js +++ b/js/lists.js @@ -274,9 +274,19 @@ PermissionScope.prototype.diff = function(other) { // code, and I gain by making local references to global variables, which // is better if done once, which would happen for each recursive call // otherwise. +// +// rhill 2014-04-03: With the addition of ubiquitous whitelisting, the function +// has grown to be quite large. The logic is relatively simple, though for an +// outsider it may appear hairy. Now re. "I unwind it completely here", well +// I need to come up with some numbers really. Who knows, maybe the javascript +// engine optimize better when code paths are at a minimum? Specifically, the +// code handling strict blocking is repeated four times, encapsulating it into +// its own function would help tidy up the code, it all depends how much +// performance is negatively affected, if any. Need to measure. PermissionScope.prototype.evaluate = function(type, hostname) { var httpsb = HTTPSB; + var ubiquitousWhitelist = httpsb.ubiquitousWhitelist; var ubiquitousBlacklist = httpsb.ubiquitousBlacklist; var blacklist = this.black.list; var whitelist = this.white.list; @@ -304,8 +314,7 @@ PermissionScope.prototype.evaluate = function(type, hostname) { cellKey = '*|' + hostname; if ( whitelist[cellKey] ) { - // If strict blocking, the type column must not be - // blacklisted + // If strict blocking, the type column must not be blacklisted if ( strictBlocking ) { i = 0; while ( parent = parents[i++] ) { @@ -326,10 +335,36 @@ PermissionScope.prototype.evaluate = function(type, hostname) { } return 'gpt'; } - if ( blacklist[cellKey] || (!graylist[cellKey] && ubiquitousBlacklist.test(hostname)) ) { + if ( blacklist[cellKey] ) { return 'rpt'; } - + if ( !graylist[cellKey] ) { + if ( ubiquitousWhitelist.test(hostname) ) { + // If strict blocking, the type column must not be blacklisted + if ( strictBlocking ) { + i = 0; + while ( parent = parents[i++] ) { + cellKey = type + '|' + parent; + if ( whitelist[cellKey] ) { + return 'gpt'; + } + if ( blacklist[cellKey] ) { + return 'rpt'; + } + } + if ( whitelist[typeKey] ) { + return 'gpt'; + } + if ( blacklist[typeKey] ) { + return 'rpt'; + } + } + return 'gpt'; + } + if ( ubiquitousBlacklist.test(hostname) ) { + return 'rpt'; + } + } // rhill 2013-12-18: // If the type is blocked and strict blocking is on, // than the only way for the cell to be whitelisted is @@ -345,8 +380,7 @@ PermissionScope.prototype.evaluate = function(type, hostname) { } cellKey = '*|' + parent; if ( whitelist[cellKey] ) { - // If strict blocking, the type column must not be - // blacklisted + // If strict blocking, the type column must not be blacklisted if ( strictBlocking ) { while ( parent = parents[i++] ) { cellKey = type + '|' + parent; @@ -366,11 +400,36 @@ PermissionScope.prototype.evaluate = function(type, hostname) { } return 'gpt'; } - if ( blacklist[cellKey] || (!graylist[cellKey] && ubiquitousBlacklist.test(parent)) ) { + if ( blacklist[cellKey] ) { return 'rpt'; } + if ( !graylist[cellKey] ) { + if ( ubiquitousWhitelist.test(parent) ) { + // If strict blocking, the type column must not be blacklisted + if ( strictBlocking ) { + while ( parent = parents[i++] ) { + cellKey = type + '|' + parent; + if ( whitelist[cellKey] ) { + return 'gpt'; + } + if ( blacklist[cellKey] ) { + return 'rpt'; + } + } + if ( whitelist[typeKey] ) { + return 'gpt'; + } + if ( blacklist[typeKey] ) { + return 'rpt'; + } + } + return 'gpt'; + } + if ( ubiquitousBlacklist.test(parent) ) { + return 'rpt'; + } + } } - // indirect: specific type, any hostname if ( whitelist[typeKey] ) { return 'gpt'; @@ -390,9 +449,17 @@ PermissionScope.prototype.evaluate = function(type, hostname) { if ( whitelist[cellKey] ) { return 'gdt'; } - if ( blacklist[cellKey] || (!graylist[cellKey] && ubiquitousBlacklist.test(hostname)) ) { + if ( blacklist[cellKey] ) { return 'rdt'; } + if ( !graylist[cellKey] ) { + if ( ubiquitousWhitelist.test(hostname) ) { + return 'gdt'; + } + if ( ubiquitousBlacklist.test(hostname) ) { + return 'rdt'; + } + } // indirect: parent hostname nodes parents = httpsb.URI.parentHostnamesFromHostname(hostname); i = 0; @@ -402,9 +469,17 @@ PermissionScope.prototype.evaluate = function(type, hostname) { if ( whitelist[cellKey] ) { return 'gpt'; } - if ( blacklist[cellKey] || (!graylist[cellKey] && ubiquitousBlacklist.test(parent)) ) { + if ( blacklist[cellKey] ) { return 'rpt'; } + if ( !graylist[cellKey] ) { + if ( ubiquitousWhitelist.test(parent) ) { + return 'gpt'; + } + if ( ubiquitousBlacklist.test(parent) ) { + return 'rpt'; + } + } } // indirect: any type, any hostname if ( whitelist['*|*'] ) { @@ -456,9 +531,12 @@ PermissionScope.prototype.removeRule = function(listKey, type, hostname) { PermissionScope.prototype.whitelist = function(type, hostname) { var key = type + '|' + hostname; var changed = false; - changed = this.white.addOne(key) || changed; changed = this.black.removeOne(key) || changed; changed = this.gray.removeOne(key) || changed; + // Avoid duplicating ubiquitously whitelisted entries + if ( type !== '*' || !HTTPSB.ubiquitousWhitelist.test(hostname) ) { + changed = this.white.addOne(key) || changed; + } return changed; }; @@ -469,7 +547,7 @@ PermissionScope.prototype.blacklist = function(type, hostname) { var changed = false; changed = this.white.removeOne(key) || changed; changed = this.gray.removeOne(key) || changed; - // Avoid duplicating read-only blacklisted entries + // Avoid duplicating ubiquitously blacklisted entries // TODO: Is this really a good idea? If user explicitly blocked an entry // which is already in read-only blacklist (after graylisting or // whitelisting it), user expects entry to still be blacklisted if ever @@ -493,8 +571,10 @@ PermissionScope.prototype.graylist = function(type, hostname) { // rhill 2013-10-25: special case, we expressly graylist only if the // key is '*' and hostname is found in read-only blacklist, so that the // express graylisting occults the read-only blacklist status. - if ( type === '*' && HTTPSB.ubiquitousBlacklist.test(hostname) ) { - changed = this.gray.addOne(key) || changed; + if ( type === '*' ) { + if ( HTTPSB.ubiquitousBlacklist.test(hostname) || HTTPSB.ubiquitousWhitelist.test(hostname) ) { + changed = this.gray.addOne(key) || changed; + } } return changed; }; diff --git a/js/storage.js b/js/storage.js index ad3d6b4..6636f30 100644 --- a/js/storage.js +++ b/js/storage.js @@ -21,69 +21,6 @@ /******************************************************************************/ -// This object allows me to perform read-modify-write operations on -// objects in chrome.store (otherwise not reliable because of asynchronicity). -// Arguments for acquire() are same as with chrome.storage.{local|sync}.get(), -// and release() *must* be called when done with stored object (or else changes -// won't be committed). - -var storageBufferer = { - lock: 0, - store: {}, - callbacks: [], - acquire: function(arg, callback) { - this.lock++; - var wantedKeys; - if ( typeof arg === 'string' ) { - wantedKeys = [arg]; - } else if ( arg instanceof Array ) { - wantedKeys = arg.slice(0); - } else { - wantedKeys = Object.keys(arg); - } - var i = wantedKeys.length; - while ( i-- ) { - if ( wantedKeys[i] in this.store ) { - wantedKeys.splice(i, 1); - } - } - if ( wantedKeys.length ) { - var self = this; - this.callbacks.push(callback); - chrome.storage.local.get(arg, function(store) { - var i = wantedKeys.length; - var key; - while ( i-- ) { - key = wantedKeys[i]; - if ( key in store ) { - self.store[key] = store[key]; - } else if ( typeof arg === 'object' && key in arg ) { - self.store[key] = arg[key]; - } - } - while ( self.callbacks.length ) { - (self.callbacks.pop())(self.store); - } - }); - } else { - callback(this.store); - } - }, - release: function() { - this.lock--; - console.assert(this.lock >= 0, 'storageBufferer.lock is negative!'); - if ( this.lock === 0 ) { - chrome.storage.local.set(this.store, getBytesInUse); - // rhill 20131017: Once all is persisted, no need to hold onto - // whatever is in the store, this reduce memory footprint of - // exension. - this.store = {}; - } - } -}; - -/******************************************************************************/ - function getBytesInUseHandler(bytesInUse) { HTTPSB.storageUsed = bytesInUse; } @@ -170,125 +107,174 @@ function loadUserLists() { /******************************************************************************/ -function loadRemoteBlacklists() { - // Get remote blacklist data (which may be saved locally). - // No need for storageBufferer.acquire() here because the fetched data - // won't be modified. - // rhill 2013-12-10: no need to use storageBufferer. - chrome.storage.local.get( - { 'remoteBlacklists': HTTPSB.remoteBlacklists }, - loadRemoteBlacklistsHandler - ); -} +HTTPSB.loadUbiquitousWhitelists = function() { + var parseUbiquitousWhitelist = function(details) { + var httpsb = HTTPSB; + var ubiquitousWhitelist = httpsb.ubiquitousWhitelist; + var raw = details.content.toLowerCase(); + var rawEnd = raw.length; + var lineBeg = 0; + var lineEnd; + var line, pos; + ubiquitousWhitelist.reset(); + while ( lineBeg < rawEnd ) { + lineEnd = raw.indexOf('\n', lineBeg); + if ( lineEnd < 0 ) { + lineEnd = rawEnd; + } + line = raw.slice(lineBeg, lineEnd); + lineBeg = lineEnd + 1; + pos = line.indexOf('#'); + if ( pos >= 0 ) { + line = line.slice(0, pos); + } + line = line.trim(); + if ( !line.length ) { + continue; + } + ubiquitousWhitelist.add(line); + } + ubiquitousWhitelist.freeze(); + }; -/******************************************************************************/ + var onMessageHandler = function(request) { + if ( !request || !request.what ) { + return; + } + if ( request.what === 'userUbiquitousWhitelistLoaded' ) { + onLoadedHandler(request); + } + }; -// rhill 2013-12-10: preset blacklists can now be reloaded after they have -// been loaded, and the resulting preset blacklisted entries might differ from -// the original. -// This means, as opposed to the first time we load, there might be entries to -// remove. -// -// So this will work this way: -// - Set all existing entries as `false`. -// - Reload will create or mark all valid entries as `true`. -// - Post-reload, all entries which are false are removed. This is not really -// necessary, but I expect the result would be reduced memory footprint -// without having to reload the extension (maybe this is the reason the user -// disabled one or more preset blacklists). - -function loadRemoteBlacklistsHandler(store) { - var httpsb = HTTPSB; + var onLoadedHandler = function(details) { + if ( !details.error ) { + parseUbiquitousWhitelist(details); + } + chrome.runtime.onMessage.removeListener(onMessageHandler); + }; - // rhill 2013-12-10: set all existing entries to `false`. - httpsb.ubiquitousBlacklist.reset(); - httpsb.abpFilters.reset(); + chrome.runtime.onMessage.addListener(onMessageHandler); - // Load each preset blacklist which is not disabled. - for ( var location in store.remoteBlacklists ) { + // ONLY the user decides what to whitelist uniquitously, so no need + // for code to handle 3rd-party lists. + HTTPSB.assets.get( + 'assets/user/ubiquitous-whitelisted-hosts.txt', + 'userUbiquitousWhitelistLoaded' + ); +}; - if ( !store.remoteBlacklists.hasOwnProperty(location) ) { - continue; - } +/******************************************************************************/ - // rhill 2014-01-24: HTTPSB-maintained lists sit now in their - // own directory, "asset/httpsb/". Ensure smooth transition. - // TODO: Remove this code when everybody upgraded beyond 0.7.7.1 - if ( location === 'assets/httpsb-blacklist.txt' && - store.remoteBlacklists[location].off === true ) - { - // In case it was already processed - httpsb.remoteBlacklists['assets/httpsb/blacklist.txt'].off = true; - // In case it was not yet processed - store.remoteBlacklists['assets/httpsb/blacklist.txt'].off = true; - } +HTTPSB.loadUbiquitousBlacklists = function() { + var blacklists; + var blacklistLoadCount; + var obsoleteBlacklists = []; - // If loaded list location is not part of default list locations, - // remove its entry from local storage. - if ( !httpsb.remoteBlacklists[location] ) { - chrome.runtime.sendMessage({ - what: 'localRemoveRemoteBlacklist', - location: location - }); - continue; + var onMessageHandler = function(details) { + if ( !details || !details.what ) { + return; } + if ( details.what === 'mergeBlacklistedHosts' ) { + mergeBlacklist(details); + } + }; - // Store details of this preset blacklist - httpsb.remoteBlacklists[location] = store.remoteBlacklists[location]; + var removeObsoleteBlacklistsHandler = function(store) { + if ( !store.remoteBlacklists ) { + return; + } + var location; + while ( location = obsoleteBlacklists.pop() ) { + delete store.remoteBlacklists[location]; + } + chrome.storage.local.set(store); + }; - // rhill 2013-12-09: - // Ignore list if disabled - // https://github.com/gorhill/httpswitchboard/issues/78 - if ( store.remoteBlacklists[location].off ) { - continue; + var removeObsoleteBlacklists = function() { + if ( obsoleteBlacklists.length === 0 ) { + return; } + chrome.storage.local.get( + { 'remoteBlacklists': HTTPSB.remoteBlacklists }, + removeObsoleteBlacklistsHandler + ); + }; - HTTPSB.assets.get(location, 'mergeBlacklistedHosts'); - } + var mergeBlacklist = function(details) { + HTTPSB.mergeBlacklistedHosts(details); + blacklistLoadCount -= 1; + if ( blacklistLoadCount === 0 ) { + loadBlacklistsEnd(); + } + }; - // This is to wake up post-process tasks once all blacklists are loaded. - asyncJobQueue.add( - 'loadUbiquitousBlacklistCompleted', - null, - onLoadUbiquitousBlacklistCompleted, - 1000, - false - ); -} + var loadBlacklistsEnd = function() { + HTTPSB.ubiquitousBlacklist.freeze(); + HTTPSB.abpFilters.freeze(); + removeObsoleteBlacklists(); + chrome.runtime.onMessage.removeListener(onMessageHandler); + chrome.runtime.sendMessage({ what: 'loadUbiquitousBlacklistCompleted' }); + }; -/******************************************************************************/ + var loadBlacklistsStart = function(store) { + var httpsb = HTTPSB; + // rhill 2013-12-10: set all existing entries to `false`. + httpsb.ubiquitousBlacklist.reset(); + httpsb.abpFilters.reset(); -// This is for: -// - Efficient notifiying of listeners: only once per whole reload -// of lists (hopefully, chosen delay is enough). -// - To prevent losing efficient pruning of the blocked hosts set: since the -// blocked hosts lists are loaded asynchronously, we must delay the prunin -// to when *all* lists are loaded, in order to avoid pruning entries which -// may be needed in a list which has not yet been processed. + blacklists = store.remoteBlacklists; + var blacklistLocations = Object.keys(store.remoteBlacklists); -function onLoadUbiquitousBlacklistCompleted() { - chrome.runtime.sendMessage({ what: 'loadUbiquitousBlacklistCompleted' }); + blacklistLoadCount = blacklistLocations.length; + if ( blacklistLoadCount === 0 ) { + loadBlacklistsEnd(); + return; + } - HTTPSB.ubiquitousBlacklist.freeze(); - HTTPSB.abpFilters.freeze(); -} + // Load each preset blacklist which is not disabled. + var location; + while ( location = blacklistLocations.pop() ) { + // rhill 2014-01-24: HTTPSB-maintained lists sit now in their + // own directory, "asset/httpsb/". Ensure smooth transition. + // TODO: Remove this code when everybody upgraded beyond 0.7.7.1 + if ( location === 'assets/httpsb-blacklist.txt' && store.remoteBlacklists[location].off === true ) { + // In case it was already processed + httpsb.remoteBlacklists['assets/httpsb/blacklist.txt'].off = true; + // In case it was not yet processed + store.remoteBlacklists['assets/httpsb/blacklist.txt'].off = true; + } + // If loaded list location is not part of default list locations, + // remove its entry from local storage. + if ( !httpsb.remoteBlacklists[location] ) { + obsoleteBlacklists.push(location); + blacklistLoadCount -= 1; + continue; + } + // Store details of this preset blacklist + httpsb.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; + } + HTTPSB.assets.get(location, 'mergeBlacklistedHosts'); + } + }; -/******************************************************************************/ + chrome.runtime.onMessage.addListener(onMessageHandler); -function localRemoveRemoteBlacklist(location) { - storageBufferer.acquire('remoteBlacklists', function(store) { - if ( store.remoteBlacklists ) { - delete store.remoteBlacklists[location]; - } - // *important* - storageBufferer.release(); - console.log('HTTP Switchboard > removed cached %s', location); - }); -} + // Get remote blacklist data (which may be saved locally). + chrome.storage.local.get( + { 'remoteBlacklists': HTTPSB.remoteBlacklists }, + loadBlacklistsStart + ); +}; /******************************************************************************/ -function mergeBlacklistedHosts(details) { +HTTPSB.mergeBlacklistedHosts = function(details) { // console.log('HTTP Switchboard > mergeBlacklistedHosts from "%s": "%s..."', details.path, details.content.slice(0, 40)); var httpsb = HTTPSB; @@ -360,16 +346,7 @@ function mergeBlacklistedHosts(details) { // blacklist, user might be happy to know this information. httpsb.remoteBlacklists[details.path].entryCount = thisListCount; httpsb.remoteBlacklists[details.path].entryUsedCount = thisListUsedCount; - - // This is to wake up post-process tasks once all blacklists are loaded. - asyncJobQueue.add( - 'loadUbiquitousBlacklistCompleted', - null, - onLoadUbiquitousBlacklistCompleted, - 1000, - false - ); -} +}; /******************************************************************************/ @@ -389,12 +366,10 @@ function reloadPresetBlacklists(switches) { } // Save switch states - // rhill 2013-12-10: I don't think there is any chance of a - // read-modify-write issue here, so I won't use storageBufferer chrome.storage.local.set({ 'remoteBlacklists': presetBlacklists }, getBytesInUse); // Now force reload - loadRemoteBlacklists(); + HTTPSB.loadUbiquitousBlacklists(); } /******************************************************************************/ @@ -419,7 +394,7 @@ HTTPSB.loadPublicSuffixList = function() { 'assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat', 'publicSuffixListLoaded' ); -} +}; /******************************************************************************/ @@ -427,7 +402,7 @@ HTTPSB.loadPublicSuffixList = function() { // logically related code. HTTPSB.reloadAllLocalAssets = function() { - loadRemoteBlacklists(); + this.loadUbiquitousBlacklists(); this.loadPublicSuffixList(); this.reloadAllPresets(); }; @@ -439,7 +414,8 @@ HTTPSB.reloadAllLocalAssets = function() { function load() { loadUserSettings(); loadUserLists(); - loadRemoteBlacklists(); + HTTPSB.loadUbiquitousBlacklists(); + HTTPSB.loadUbiquitousWhitelists(); HTTPSB.loadPublicSuffixList(); HTTPSB.reloadAllPresets(); getBytesInUse(); diff --git a/js/ubiquitous-rules.js b/js/ubiquitous-rules.js index 363e93e..9bf6257 100644 --- a/js/ubiquitous-rules.js +++ b/js/ubiquitous-rules.js @@ -25,9 +25,11 @@ /******************************************************************************/ -var userListPath = 'assets/user/ubiquitous-blacklisted-hosts.txt'; +var userBlacklistPath = 'assets/user/ubiquitous-blacklisted-hosts.txt'; +var userWhitelistPath = 'assets/user/ubiquitous-whitelisted-hosts.txt'; var userListHref = '#userUbiquitousBlacklistedHostsPrompt'; var cachedUserUbiquitousBlacklistedHosts = ''; +var cachedUserUbiquitousWhitelistedHosts = ''; var selectedBlacklistsHash = ''; /******************************************************************************/ @@ -69,7 +71,10 @@ function renderBlacklists() { var httpsb = gethttpsb(); - $('#ubiquitousBlacklistCount').text(renderNumber(httpsb.ubiquitousBlacklist.count)); + $('#ubiquitousListsOfBlockedHostsPrompt2').text( + chrome.i18n.getMessage('ubiquitousListsOfBlockedHostsPrompt2') + .replace('{{ubiquitousBlacklistCount}}', renderNumber(httpsb.ubiquitousBlacklist.count)) + ); var blacklists = httpsb.remoteBlacklists; var ul = $('#blacklists'); @@ -86,7 +91,7 @@ function renderBlacklists() { child.prop('checked', !blacklist.off); child = $('a', li); // Special rendering: user list - if ( blacklistName === userListPath ) { + if ( blacklistName === userBlacklistPath ) { child.attr('href', userListHref); child.text($(userListHref).text()); } else { @@ -100,8 +105,10 @@ function renderBlacklists() { ul.prepend(li); } $('#parseAllABPFilters').attr('checked', httpsb.userSettings.parseAllABPFilters === true); - $('#parseAllABPFilters + span > span').text(renderNumber(httpsb.abpFilters.getFilterCount())); - + $('#ubiquitousParseAllABPFiltersPrompt2').text( + chrome.i18n.getMessage("ubiquitousParseAllABPFiltersPrompt2") + .replace('{{abpFilterCount}}', renderNumber(httpsb.abpFilters.getFilterCount())) + ); selectedBlacklistsHash = getSelectedBlacklistsHash(); } @@ -136,23 +143,72 @@ function getSelectedBlacklistsHash() { // This is to give a visual hint that the selection of blacklists has changed. function selectedBlacklistsChanged() { - $('#blacklistsApply').attr('disabled', getSelectedBlacklistsHash() === selectedBlacklistsHash); + $('#blacklistsApply').attr( + 'disabled', + getSelectedBlacklistsHash() === selectedBlacklistsHash + ); } // This is to give a visual hint that the content of user blacklist has changed. function userBlacklistChanged() { - $('#userBlacklistApply') - .attr('disabled', $('#userUbiquitousBlacklistedHosts') - .val() - .trim() === cachedUserUbiquitousBlacklistedHosts); + $('#userUbiquitousBlacklistApply') + .attr( + 'disabled', + $('#userUbiquitousBlacklistedHosts').val().trim() === cachedUserUbiquitousBlacklistedHosts + ); selectedBlacklistsChanged(); } /******************************************************************************/ +function userWhitelistChanged() { + $('#userUbiquitousWhitelistApply') + .attr( + 'disabled', + $('#userUbiquitousWhitelistedHosts').val().trim() === cachedUserUbiquitousWhitelistedHosts + ); +} + +/******************************************************************************/ + function renderUserBlacklist() { - gethttpsb().assets.get(userListPath, 'dashboardGetUbiquitousUserBlacklist'); + var onMessageHandler = function(details) { + if ( !details || !details.what ) { + return; + } + if ( details.what !== 'dashboardGetUbiquitousUserBlacklist' ) { + return; + } + if ( !details.error ) { + cachedUserUbiquitousBlacklistedHosts = details.content.trim(); + $('#userUbiquitousBlacklistedHosts').val(details.content); + renderBlacklists(); + } + chrome.runtime.onMessage.removeListener(onMessageHandler); + }; + chrome.runtime.onMessage.addListener(onMessageHandler); + gethttpsb().assets.get(userBlacklistPath, 'dashboardGetUbiquitousUserBlacklist'); +} + +/******************************************************************************/ + +function renderUserWhitelist() { + var onMessageHandler = function(details) { + if ( !details || !details.what ) { + return; + } + if ( details.what !== 'dashboardGetUbiquitousUserWhitelist' ) { + return; + } + if ( !details.error ) { + cachedUserUbiquitousWhitelistedHosts = details.content.trim(); + $('#userUbiquitousWhitelistedHosts').val(details.content); + } + chrome.runtime.onMessage.removeListener(onMessageHandler); + }; + chrome.runtime.onMessage.addListener(onMessageHandler); + gethttpsb().assets.get(userWhitelistPath, 'dashboardGetUbiquitousUserWhitelist'); } /******************************************************************************/ @@ -170,7 +226,7 @@ function blacklistsApplyHandler() { while ( i-- ) { path = $(lis[i]).children('a').attr('href'); if ( path === userListHref ) { - path = userListPath; + path = userBlacklistPath; } switches.push({ location: path, @@ -193,9 +249,60 @@ function abpFiltersCheckboxChanged() { /******************************************************************************/ +function appendToUserBlacklistFromFile() { + var input = $('').attr({ + type: 'file', + accept: 'text/plain' + }); + var fileReaderOnLoadHandler = function() { + var textarea = $('#userUbiquitousBlacklistedHosts'); + textarea.val(textarea.val() + '\n' + this.result); + userBlacklistChanged(); + }; + var filePickerOnChangeHandler = function() { + $(this).off('change', filePickerOnChangeHandler); + var file = this.files[0]; + if ( !file ) { + return; + } + if ( file.type.indexOf('text') !== 0 ) { + return; + } + var fr = new FileReader(); + fr.onload = fileReaderOnLoadHandler; + fr.readAsText(file); + input.off('change', filePickerOnChangeHandler); + }; + input.on('change', filePickerOnChangeHandler); + input.trigger('click'); +} + +function exportUserBlacklistToFile() { + chrome.downloads.download({ + 'url': 'data:text/plain,' + encodeURIComponent($('#userUbiquitousBlacklistedHosts').val()), + 'filename': 'ubiquitous-blacklisted-hosts.txt', + 'saveAs': true + }); +} + function userBlacklistApplyHandler() { + var onMessageHandler = function(details) { + if ( !details || !details.what ) { + return; + } + if ( details.what !== 'dashboardPutUbiquitousUserBlacklist' ) { + return; + } + if ( !details.error ) { + cachedUserUbiquitousBlacklistedHosts = details.content.trim(); + userBlacklistChanged(); + blacklistsApplyHandler(); + } + chrome.runtime.onMessage.removeListener(onMessageHandler); + }; + chrome.runtime.onMessage.addListener(onMessageHandler); gethttpsb().assets.put( - userListPath, + userBlacklistPath, $('#userUbiquitousBlacklistedHosts').val(), 'dashboardPutUbiquitousUserBlacklist' ); @@ -203,69 +310,75 @@ function userBlacklistApplyHandler() { /******************************************************************************/ -function fileReaderOnLoadHandler() { - var textarea = $('#userUbiquitousBlacklistedHosts'); - textarea.val(textarea.val() + '\n' + this.result); - userBlacklistChanged(); -} - -function filePickerOnChangeHandler() { - $(this).off('change', filePickerOnChangeHandler); - var file = this.files[0]; - if ( !file ) { - return; - } - if ( file.type.indexOf('text') !== 0 ) { - return; - } - var fr = new FileReader(); - fr.onload = fileReaderOnLoadHandler; - fr.readAsText(file); -} - -function appentToUserBlacklistFromFile() { +function appendToUserWhitelistFromFile() { var input = $('').attr({ type: 'file', accept: 'text/plain' - }); + var fileReaderOnLoadHandler = function() { + var textarea = $('#userUbiquitousWhitelistedHosts'); + textarea.val(textarea.val() + '\n' + this.result); + userWhitelistChanged(); + }; + var filePickerOnChangeHandler = function() { + $(this).off('change', filePickerOnChangeHandler); + var file = this.files[0]; + if ( !file ) { + return; + } + if ( file.type.indexOf('text') !== 0 ) { + return; + } + var fr = new FileReader(); + fr.onload = fileReaderOnLoadHandler; + fr.readAsText(file); + input.off('change', filePickerOnChangeHandler); + }; input.on('change', filePickerOnChangeHandler); input.trigger('click'); } -/******************************************************************************/ - -function exportUserBlacklistToFile() { +function exportUserWhitelistToFile() { chrome.downloads.download({ - 'url': 'data:text/plain,' + encodeURIComponent($('#userUbiquitousBlacklistedHosts').val()), - 'filename': 'ubiquitous-blacklisted-hosts.txt', + 'url': 'data:text/plain,' + encodeURIComponent($('#userUbiquitousWhitelistedHosts').val()), + 'filename': 'ubiquitous-whitelisted-hosts.txt', 'saveAs': true }); } +function userWhitelistApplyHandler() { + var httpsb = gethttpsb(); + var onMessageHandler = function(details) { + if ( !details || !details.what ) { + return; + } + if ( details.what !== 'dashboardPutUbiquitousUserWhitelist' ) { + return; + } + if ( !details.error ) { + cachedUserUbiquitousWhitelistedHosts = details.content.trim(); + userWhitelistChanged(); + httpsb.loadUbiquitousWhitelists(); + } + chrome.runtime.onMessage.removeListener(onMessageHandler); + }; + chrome.runtime.onMessage.addListener(onMessageHandler); + httpsb.assets.put( + userWhitelistPath, + $('#userUbiquitousWhitelistedHosts').val(), + 'dashboardPutUbiquitousUserWhitelist' + ); +} + /******************************************************************************/ -function onMessageHandler(request, sender) { - if ( request && request.what ) { - switch ( request.what ) { +function onMessageHandler(details) { + if ( details && details.what ) { + switch ( details.what ) { case 'loadUbiquitousBlacklistCompleted': renderBlacklists(); selectedBlacklistsChanged(); break; - case 'dashboardGetUbiquitousUserBlacklist': - if ( !request.error ) { - cachedUserUbiquitousBlacklistedHosts = request.content.trim(); - $('#userUbiquitousBlacklistedHosts').val(request.content); - renderBlacklists(); - } - break; - case 'dashboardPutUbiquitousUserBlacklist': - if ( !request.error ) { - cachedUserUbiquitousBlacklistedHosts = request.content.trim(); - userBlacklistChanged(); - blacklistsApplyHandler(); - } - break; } } } @@ -277,15 +390,22 @@ $(function() { $('#blacklists').on('change', '.blacklistDetails', selectedBlacklistsChanged); $('#blacklistsApply').on('click', blacklistsApplyHandler); $('#parseAllABPFilters').on('change', abpFiltersCheckboxChanged); - $('#userBlacklistApply').on('click', userBlacklistApplyHandler); - $('#userUbiquitousBlacklistedHosts').on('input propertychange', userBlacklistChanged); - $('#importUserBlacklistFromFile').on('click', appentToUserBlacklistFromFile); + + $('#importUserBlacklistFromFile').on('click', appendToUserBlacklistFromFile); $('#exportUserBlacklistToFile').on('click', exportUserBlacklistToFile); + $('#userUbiquitousBlacklistedHosts').on('input propertychange', userBlacklistChanged); + $('#userUbiquitousBlacklistApply').on('click', userBlacklistApplyHandler); + + $('#importUserWhitelistFromFile').on('click', appendToUserWhitelistFromFile); + $('#exportUserWhitelistToFile').on('click', exportUserWhitelistToFile); + $('#userUbiquitousWhitelistedHosts').on('input propertychange', userWhitelistChanged); + $('#userUbiquitousWhitelistApply').on('click', userWhitelistApplyHandler); chrome.runtime.onMessage.addListener(onMessageHandler); renderBlacklists(); renderUserBlacklist(); + renderUserWhitelist(); }); /******************************************************************************/ diff --git a/ubiquitous-rules.html b/ubiquitous-rules.html index e37304b..c87ee99 100644 --- a/ubiquitous-rules.html +++ b/ubiquitous-rules.html @@ -22,7 +22,7 @@ .dim { color: #888; } -#userUbiquitousBlacklistedHosts { +.userUbiquitousHosts { width: 32em; height: 16em; } @@ -31,45 +31,42 @@ -

What is this?

+

-

“Ubiquitous rules” are rules which applies everywhere, i.e. in all scopes.

+

-

All lists of blocked hosts are loaded as ubiquitous rules, hence these hosts are blacklisted in all scopes.

- ? preset blacklisted hostnames from (number of distinct entries in parentheses): +

+

-

Your list of blocked hosts

+

-

-

One hostname per line. Line prefixed with ‘#’ will be ignored.

- -

+

+

+ +

+
+ +

+
+

+

+ +