this fixes 202

This commit is contained in:
gorhill 2014-09-14 16:20:40 -04:00
parent 1b033d38d7
commit ee81436ff5
19 changed files with 604 additions and 316 deletions

View File

@ -207,22 +207,14 @@
"message":"Änderungen anwenden",
"description":"English: Apply changes"
},
"logBlockedRequestsPrompt":{
"logNetRequestsPrompt":{
"message":"Protokollieren von blockierten Elementen aktivieren",
"description":"English: Enable the logging of blocked requests"
"description":"English: Enable the logging of network requests"
},
"logBlockedRequestsHelp":{
"logNetRequestsHelp":{
"message":"Du kannst dir die Details von blockierten Anfragen anschauen, wenn du diese Option aktivierst. Das Protokollieren von blockierten Anfragen erhöht den Speicherbedarf von <i>µBlock<\/i>. Da dieses Feature von vielen Benutzern wohl nicht genutzt werden wird, ist es standardmäßig deaktiviert.",
"description":"English: see _locales\/en\/messages.log"
},
"logAllowedRequestsPrompt":{
"message":"Protokollieren von nicht-blockierten Elementen aktivieren",
"description":"English: Enable the logging of non-blocked requests"
},
"logAllowedRequestsHelp":{
"message":"Du kannst dir die Details von nicht-blockierten Anfragen anschauen, wenn du diese Option aktivierst. Das Protokollieren von nicht-blockierten Anfragen erhöht den Speicherbedarf von µBlock. Da dieses Feature von vielen Benutzern wohl nicht genutzt werden wird, ist es standardmäßig deaktiviert.",
"description":"English: see _locales\/en\/messages.log"
},
"logBlockedRequestsHeader":{
"message":"Blockierte Anfragen",
"description":"English: Blocked requests"

View File

@ -207,20 +207,12 @@
"message":"Apply changes",
"description":"English: Apply changes"
},
"logBlockedRequestsPrompt":{
"message":"Enable the logging of blocked requests",
"description":"English: Enable the logging of blocked requests"
"logNetRequestsPrompt":{
"message":"Enable the logging of network requests",
"description":"English: Enable the logging of network requests"
},
"logBlockedRequestsHelp":{
"message":"You can inspect the details of blocked requests if you wish by enabling this option. The logging of blocked requests increases the memory footprint of µBlock. Since many users will never use this feature, it is disabled by default.",
"description":"English: see _locales\/en\/messages.log"
},
"logAllowedRequestsPrompt":{
"message":"Enable the logging of non-blocked requests",
"description":"English: Enable the logging of non-blocked requests"
},
"logAllowedRequestsHelp":{
"message":"You can inspect the details of non-blocked requests if you wish by enabling this option. The logging of non-blocked requests increases the memory footprint of µBlock. Since many users will never use this feature, it is disabled by default.",
"logNetRequestsHelp":{
"message":"You can inspect the details of network requests if you wish by enabling this option. The logging of network requests increases the memory footprint of µBlock. Since many users will never use this feature, it is disabled by default.",
"description":"English: see _locales\/en\/messages.log"
},
"logBlockedRequestsHeader":{

View File

@ -207,20 +207,12 @@
"message":"Appliquer",
"description":"English: Apply changes"
},
"logBlockedRequestsPrompt":{
"message":"Activer la journalisation des requêtes bloquées",
"description":"English: Enable the logging of blocked requests"
"logNetRequestsPrompt":{
"message":"Activer la journalisation des requêtes",
"description":"English: Enable the logging of network requests"
},
"logBlockedRequestsHelp":{
"message":"Vous pouvez inspecter les détails des requêtes bloquées en activant cette option. La journalisation des requêtes bloquées augmente l'empreinte mémoire utilisée par µBlock. Puisque que bon nombre d'utilisateurs n'utiliseront jamais cette fonctionnalité, elle est désactivée par défaut.",
"description":"English: see _locales\/en\/messages.log"
},
"logAllowedRequestsPrompt":{
"message":"Activer la journalisation des requêtes autorisées",
"description":"English: Enable the logging of non-blocked requests"
},
"logAllowedRequestsHelp":{
"message":"Vous pouvez inspecter les détails des requêtes autorisées en activant cette option. La journalisation des requêtes autorisées augmente l'empreinte mémoire utilisée par µBlock. Puisque que bon nombre d'utilisateurs n'utiliseront jamais cette fonctionnalité, elle est désactivée par défaut.",
"logNetRequestsHelp":{
"message":"Vous pouvez inspecter les détails des requêtes en activant cette option. La journalisation des requêtes augmente l'empreinte mémoire utilisée par µBlock. Puisque que bon nombre d'utilisateurs n'utiliseront jamais cette fonctionnalité, elle est désactivée par défaut.",
"description":"English: see _locales\/en\/messages.log"
},
"logBlockedRequestsHeader":{

View File

@ -14,8 +14,8 @@
<script src="js/liquid-dict.js"></script>
<script src="js/utils.js"></script>
<script src="js/assets.js"></script>
<script src="js/net-filters.js"></script>
<script src="js/cosmetic-filters.js"></script>
<script src="js/net-filtering.js"></script>
<script src="js/cosmetic-filtering.js"></script>
<script src="js/ublock.js"></script>
<script src="js/messaging.js"></script>
<script src="js/profiler.js"></script>

View File

@ -27,6 +27,8 @@
µBlock.asyncJobs = (function() {
/******************************************************************************/
var processJobs = function() {
asyncJobManager.process();
};
@ -45,6 +47,8 @@ AsyncJobEntry.prototype.destroy = function() {
this.callback = null;
};
/******************************************************************************/
var AsyncJobManager = function() {
this.timeResolution = 200;
this.jobs = {};
@ -54,7 +58,15 @@ var AsyncJobManager = function() {
this.timerWhen = Number.MAX_VALUE;
};
/******************************************************************************/
AsyncJobManager.prototype.restartTimer = function() {
// TODO: Another way to do this is to extract the keys, sort the keys
// in chronological order, than pick the first entry to get the next
// time at which we want a time event to fire. Completely unsure the
// overhead of extracting keys/sorting is less than what is below.
// I could also keep the keys ordered, and use binary search when adding
// a new job.
var when = Number.MAX_VALUE;
var jobs = this.jobs, job;
for ( var jobName in jobs ) {
@ -68,6 +80,10 @@ AsyncJobManager.prototype.restartTimer = function() {
// Quantize time value
when = Math.floor((when + this.timeResolution - 1) / this.timeResolution) * this.timeResolution;
// TODO: Maybe use chrome.alarms() API when the next job is at more than
// one minute in the future... From reading about it, chrome.alarms() is
// smarter in that it will fire the event only when the browser is not
// too busy. (through XAL to abstract API specificities)
if ( when < this.timerWhen ) {
clearTimeout(this.timerId);
this.timerWhen = when;
@ -75,6 +91,8 @@ AsyncJobManager.prototype.restartTimer = function() {
}
};
/******************************************************************************/
AsyncJobManager.prototype.add = function(name, data, callback, delay, recurrent) {
var job = this.jobs[name];
if ( !job ) {
@ -94,6 +112,22 @@ AsyncJobManager.prototype.add = function(name, data, callback, delay, recurrent)
this.restartTimer();
};
/******************************************************************************/
AsyncJobManager.prototype.remove = function(jobName) {
if ( this.jobs.hasOwnProperty(jobName) === false ) {
return;
}
var job = this.jobs[jobName];
delete this.jobs[jobName];
job.destroy();
this.jobCount--;
this.jobJunkyard.push(job);
this.restartTimer();
};
/******************************************************************************/
AsyncJobManager.prototype.process = function() {
this.timerId = null;
this.timerWhen = Number.MAX_VALUE;
@ -120,9 +154,13 @@ AsyncJobManager.prototype.process = function() {
this.restartTimer();
};
/******************************************************************************/
// Only one instance
var asyncJobManager = new AsyncJobManager();
/******************************************************************************/
// Publish
return asyncJobManager;
@ -133,12 +171,12 @@ return asyncJobManager;
// Update visual of extension icon.
// A time out is used to coalesce adjacent requests to update badge.
µBlock.updateBadgeAsync = function(tabId) {
if ( tabId < 0 ) {
return;
}
var µb = this;
var updateBadge = function() {
µBlock.updateBadgeAsync = (function(){
var µb = µBlock;
// Cache callback definition, it was a bad idea to define this one inside
// updateBadgeAsync
var updateBadge = function(tabId) {
var pageStore = µb.pageStoreFromTabId(tabId);
if ( pageStore ) {
pageStore.updateBadge();
@ -150,5 +188,13 @@ return asyncJobManager;
''
);
};
this.asyncJobs.add('updateBadge-' + tabId, tabId, updateBadge, 250);
};
var updateBadgeAsync = function(tabId) {
if ( tabId < 0 ) {
return;
}
µb.asyncJobs.add('updateBadge-' + tabId, tabId, updateBadge, 250);
};
return updateBadgeAsync;
})();

View File

@ -41,8 +41,7 @@ return {
autoUpdate: true,
collapseBlocked: true,
externalLists: '',
logBlockedRequests: false,
logAllowedRequests: false,
logRequests: false,
parseAllABPHideFilters: true,
showIconBadge: true
},

View File

@ -56,16 +56,7 @@ var uBlockMessaging = (function(name){
};
var start = function(name) {
port = chrome.runtime.connect({
name: name +
'/' +
String.fromCharCode(
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0
)
});
port = chrome.runtime.connect({ name: name });
port.onMessage.addListener(onPortMessage);
// https://github.com/gorhill/uBlock/issues/193
@ -111,7 +102,7 @@ var uBlockMessaging = (function(name){
var flushCallbacks = function() {
var callback;
for ( id in requestIdToCallbackMap ) {
for ( var id in requestIdToCallbackMap ) {
if ( requestIdToCallbackMap.hasOwnProperty(id) === false ) {
continue;
}
@ -527,63 +518,11 @@ var uBlockMessaging = (function(name){
/******************************************************************************/
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/7
// Permanent
(function() {
var messaging = uBlockMessaging;
var blockableElements = {
'embed': 'src',
'iframe': 'src',
'img': 'src',
'object': 'data'
};
// First pass
messaging.ask({ what: 'blockedRequests' }, function(details) {
var elems = document.querySelectorAll('embed,iframe,img,object');
var blockedRequests = details.blockedRequests;
var collapse = details.collapse;
var i = elems.length;
var elem, tagName, prop, src;
var selectors = [];
while ( i-- ) {
elem = elems[i];
tagName = elem.tagName.toLowerCase();
prop = blockableElements[tagName];
if ( prop === undefined ) {
continue;
}
src = elem[prop];
if ( typeof src !== 'string' || src === '' ) {
continue;
}
if ( blockedRequests[src] === undefined ) {
continue;
}
// If `!important` is not there, going back using history will
// likely cause the hidden element to re-appear.
if ( collapse ) {
if ( elem.parentNode ) {
elem.parentNode.removeChild(elem);
} else {
elem.style.setProperty('display', 'none', 'important');
}
} else {
elem.style.setProperty('visibility', 'hidden', 'important');
}
selectors.push(tagName + '[' + prop + '="' + src + '"]');
}
if ( selectors.length !== 0 ) {
messaging.tell({
what: 'injectedSelectors',
type: 'net',
hostname: window.location.hostname,
selectors: selectors
});
}
});
// Listeners to mop up whatever is otherwise missed:
// - Future requests not blocked yet
// - Elements dynamically added to the page
@ -612,11 +551,15 @@ var uBlockMessaging = (function(name){
if ( typeof src !== 'string' || src === '' ) {
return;
}
if ( src.slice(0, 4) !== 'http' ) {
return;
}
// https://github.com/gorhill/uBlock/issues/174
// Do not remove fragment from src URL
var onAnswerReceived = function(details) {
if ( !details.blocked ) {
if ( typeof details !== 'object' || details === null ) {
return;
}
// If `!important` is not there, going back using history will
@ -637,7 +580,15 @@ var uBlockMessaging = (function(name){
selectors: tagName + '[' + prop + '="' + src + '"]'
});
};
messaging.ask({ what: 'blockedRequest', url: src }, onAnswerReceived);
var details = {
what: 'filterRequest',
tagName: tagName,
requestURL: src,
pageHostname: window.location.hostname,
pageURL: window.location.href
};
messaging.ask(details, onAnswerReceived);
};
var onResourceLoaded = function(ev) {
@ -655,3 +606,95 @@ var uBlockMessaging = (function(name){
})();
/******************************************************************************/
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/7
// Executed only once
(function() {
var messaging = uBlockMessaging;
var readyProps = {
'img': 'complete'
};
var srcProps = {
'embed': 'src',
'iframe': 'src',
'img': 'src',
'object': 'data'
};
var elements = [];
var onAnswerReceived = function(details) {
if ( typeof details !== 'object' || details === null ) {
return;
}
var requests = details.requests;
var collapse = details.collapse;
var selectors = [];
var i = requests.length;
var request, elem;
while ( i-- ) {
request = requests[i];
elem = elements[request.index];
if ( collapse ) {
if ( elem.parentNode ) {
elem.parentNode.removeChild(elem);
} else {
elem.style.setProperty('display', 'none', 'important');
}
} else {
elem.style.setProperty('visibility', 'hidden', 'important');
}
selectors.push(request.tagName + '[' + srcProps[request.tagName] + '="' + request.url + '"]');
}
if ( selectors.length !== 0 ) {
messaging.tell({
what: 'injectedSelectors',
type: 'net',
hostname: window.location.hostname,
selectors: selectors
});
}
};
var requests = [];
var tagNames = ['embed','iframe','img','object'];
var elementIndex = 0;
var tagName, elems, i, elem, prop, src;
while ( tagName = tagNames.pop() ) {
elems = document.getElementsByTagName(tagName);
i = elems.length;
while ( i-- ) {
elem = elems[i];
prop = srcProps[tagName];
if ( prop === undefined ) {
continue;
}
src = elem[prop];
if ( typeof src !== 'string' || src === '' ) {
continue;
}
if ( src.slice(0, 4) !== 'http' ) {
continue;
}
requests.push({
index: elementIndex,
tagName: tagName,
url: src
});
elements[elementIndex] = elem;
elementIndex += 1;
}
}
var details = {
what: 'filterRequests',
pageURL: window.location.href,
pageHostname: window.location.hostname,
requests: requests
};
messaging.ask(details, onAnswerReceived);
})();
/******************************************************************************/

View File

@ -66,16 +66,7 @@ var uBlockMessaging = (function(name){
};
var start = function(name) {
port = chrome.runtime.connect({
name: name +
'/' +
String.fromCharCode(
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0
)
});
port = chrome.runtime.connect({ name: name });
port.onMessage.addListener(onPortMessage);
// https://github.com/gorhill/uBlock/issues/193
@ -121,7 +112,7 @@ var uBlockMessaging = (function(name){
var flushCallbacks = function() {
var callback;
for ( id in requestIdToCallbackMap ) {
for ( var id in requestIdToCallbackMap ) {
if ( requestIdToCallbackMap.hasOwnProperty(id) === false ) {
continue;
}

View File

@ -143,16 +143,7 @@ var messaging = (function(name){
};
var start = function(name) {
port = chrome.runtime.connect({
name: name +
'/' +
String.fromCharCode(
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0
)
});
port = chrome.runtime.connect({ name: name });
port.onMessage.addListener(onPortMessage);
// https://github.com/gorhill/uBlock/issues/193
@ -198,7 +189,7 @@ var messaging = (function(name){
var flushCallbacks = function() {
var callback;
for ( id in requestIdToCallbackMap ) {
for ( var id in requestIdToCallbackMap ) {
if ( requestIdToCallbackMap.hasOwnProperty(id) === false ) {
continue;
}

View File

@ -68,16 +68,7 @@ var messaging = (function(name){
};
var start = function(name) {
port = chrome.runtime.connect({
name: name +
'/' +
String.fromCharCode(
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0,
Math.random() * 0x7FFF | 0
)
});
port = chrome.runtime.connect({ name: name });
port.onMessage.addListener(onPortMessage);
// https://github.com/gorhill/uBlock/issues/193
@ -123,7 +114,7 @@ var messaging = (function(name){
var flushCallbacks = function() {
var callback;
for ( id in requestIdToCallbackMap ) {
for ( var id in requestIdToCallbackMap ) {
if ( requestIdToCallbackMap.hasOwnProperty(id) === false ) {
continue;
}

View File

@ -21,6 +21,7 @@
/* global chrome, µBlock, YaMD5 */
/******************************************************************************/
/******************************************************************************/
(function() {
@ -40,8 +41,7 @@ var getStats = function(request) {
pageAllowedRequestCount: 0,
netFilteringSwitch: false,
cosmeticFilteringSwitch: false,
logBlockedRequests: µb.userSettings.logBlockedRequests,
logAllowedRequests: µb.userSettings.logAllowedRequests
logRequests: µb.userSettings.logRequests
};
var pageStore = µb.pageStoreFromTabId(request.tabId);
if ( pageStore ) {
@ -95,6 +95,7 @@ var onMessage = function(request, sender, callback) {
})();
/******************************************************************************/
/******************************************************************************/
// contentscript-start.js
@ -136,6 +137,7 @@ var onMessage = function(request, sender, callback) {
})();
/******************************************************************************/
/******************************************************************************/
// contentscript-end.js
@ -144,9 +146,70 @@ var onMessage = function(request, sender, callback) {
var µb = µBlock;
var onMessage = function(request, sender, callback) {
/******************************************************************************/
var tagNameToRequestTypeMap = {
'embed': 'object',
'iframe': 'sub_frame',
'img': 'image',
'object': 'object'
};
/******************************************************************************/
// Evaluate many requests
var filterRequests = function(pageStore, details) {
details.pageDomain = µb.URI.domainFromHostname(details.pageHostname);
var inRequests = details.requests;
var outRequests = [];
var request, result;
var i = inRequests.length;
while ( i-- ) {
request = inRequests[i];
if ( tagNameToRequestTypeMap.hasOwnProperty(request.tagName) === false ) {
continue;
}
result = pageStore.filterRequest(
details,
tagNameToRequestTypeMap[request.tagName],
request.url
);
if ( pageStore.boolFromResult(result) ) {
outRequests.push(request);
}
}
return {
collapse: µb.userSettings.collapseBlocked,
requests: outRequests
};
};
/******************************************************************************/
// Evaluate a single request
var filterRequest = function(pageStore, details) {
if ( tagNameToRequestTypeMap.hasOwnProperty(details.tagName) === false ) {
return;
}
details.pageDomain = µb.URI.domainFromHostname(details.pageHostname);
var result = pageStore.filterRequest(
details,
tagNameToRequestTypeMap[details.tagName],
details.requestURL
);
if ( pageStore.boolFromResult(result) ) {
return { collapse: µb.userSettings.collapseBlocked };
}
};
/******************************************************************************/
var onMessage = function(details, sender, callback) {
// Async
switch ( request.what ) {
switch ( details.what ) {
default:
break;
}
@ -159,34 +222,33 @@ var onMessage = function(request, sender, callback) {
pageStore = µb.pageStoreFromTabId(sender.tab.id);
}
switch ( request.what ) {
switch ( details.what ) {
case 'retrieveGenericCosmeticSelectors':
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
response = µb.cosmeticFilteringEngine.retrieveGenericSelectors(request);
response = µb.cosmeticFilteringEngine.retrieveGenericSelectors(details);
}
break;
case 'injectedSelectors':
µb.cosmeticFilteringEngine.addToSelectorCache(request);
µb.cosmeticFilteringEngine.addToSelectorCache(details);
break;
case 'blockedRequests':
response = {
collapse: µb.userSettings.collapseBlocked,
blockedRequests: pageStore ? pageStore.blockedRequests : {}
};
// Evaluate many requests
case 'filterRequests':
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
response = filterRequests(pageStore, details);
}
break;
// Check a single request
case 'blockedRequest':
response = {
collapse: µb.userSettings.collapseBlocked,
blocked: pageStore && pageStore.blockedRequests[request.url]
};
// Evaluate a single request
case 'filterRequest':
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
response = filterRequest(pageStore, details);
}
break;
default:
return µb.messaging.defaultHandler(request, sender, callback);
return µb.messaging.defaultHandler(details, sender, callback);
}
callback(response);
@ -196,6 +258,7 @@ var onMessage = function(request, sender, callback) {
})();
/******************************************************************************/
/******************************************************************************/
// element-picker.js
@ -238,6 +301,7 @@ var onMessage = function(request, sender, callback) {
})();
/******************************************************************************/
/******************************************************************************/
// 3p-filters.js
@ -309,6 +373,7 @@ var onMessage = function(request, sender, callback) {
})();
/******************************************************************************/
/******************************************************************************/
// 1p-filters.js
@ -345,6 +410,7 @@ var onMessage = function(request, sender, callback) {
})();
/******************************************************************************/
/******************************************************************************/
// whitelist.js
@ -384,6 +450,7 @@ var onMessage = function(request, sender, callback) {
})();
/******************************************************************************/
/******************************************************************************/
// stats.js
@ -400,46 +467,48 @@ var getPageDetails = function(µb, tabId) {
if ( !pageStore ) {
return r;
}
var prepareRequests = function(requests, hasher) {
var prepareRequests = function(wantBlocked, hasher) {
var µburi = µb.URI;
var dict = pageStore.netFilteringCache.fetchAll();
var r = [];
var details, pos, hostname, domain;
for ( var requestURL in requests ) {
if ( requests.hasOwnProperty(requestURL) === false ) {
var details, pos, result, hostname, domain;
for ( var url in dict ) {
if ( dict.hasOwnProperty(url) === false ) {
continue;
}
details = requests[requestURL];
details = dict[url].data;
if ( typeof details !== 'string' ) {
continue;
}
hasher.appendStr(requestURL);
hasher.appendStr(details);
pos = details.indexOf('\t');
hostname = µburi.hostnameFromURI(requestURL);
domain = µburi.domainFromHostname(hostname);
if ( domain === '' ) {
domain = hostname;
result = details.slice(pos + 1);
if ( wantBlocked !== pageStore.boolFromResult(result) ) {
continue;
}
hasher.appendStr(url);
hasher.appendStr(details);
hostname = µburi.hostnameFromURI(url);
domain = µburi.domainFromHostname(hostname) || hostname;
r.push({
type: details.slice(0, pos),
domain: domain,
url: requestURL,
reason: details.slice(pos + 1)
url: url,
reason: result
});
}
return r;
};
var hasher = new YaMD5();
if ( µb.userSettings.logBlockedRequests ) {
r.blockedRequests = prepareRequests(pageStore.blockedRequests, hasher);
}
if ( µb.userSettings.logAllowedRequests ) {
r.allowedRequests = prepareRequests(pageStore.allowedRequests, hasher);
if ( µb.userSettings.logRequests ) {
r.blockedRequests = prepareRequests(true, hasher);
r.allowedRequests = prepareRequests(false, hasher);
}
r.hash = hasher.end();
return r;
};
/******************************************************************************/
var onMessage = function(request, sender, callback) {
var µb = µBlock;
@ -472,6 +541,7 @@ var onMessage = function(request, sender, callback) {
})();
/******************************************************************************/
/******************************************************************************/
// about.js

View File

@ -50,6 +50,7 @@
/******************************************************************************/
var runtimeIdGenerator = 1;
var nameToPortMap = {};
var nameToListenerMap = {};
var nullFunc = function(){};
@ -58,7 +59,7 @@ var nullFunc = function(){};
var listenerNameFromPortName = function(portName) {
var pos = portName.indexOf('/');
if ( pos <= 0 ) {
if ( pos === -1 ) {
return '';
}
return portName.slice(0, pos);
@ -213,14 +214,13 @@ var onDisconnect = function(port) {
var onConnect = function(port) {
// We must have a port name.
if ( !port.name ) {
throw 'µBlock> messaging.js / onConnectHandler(): no port name!';
if ( typeof port.name !== 'string' || port.name === '' ) {
console.error('µBlock> messaging.js / onConnectHandler(): no port name!');
return;
}
// Port should not already exist.
if ( nameToPortMap[port.name] ) {
throw 'µBlock> messaging.js / onConnectHandler(): port already exists!';
}
// Ensure port name is unique
port.name += '/' + runtimeIdGenerator++;
nameToPortMap[port.name] = port;
port.onMessage.addListener(onMessage);

View File

@ -28,6 +28,8 @@
/******************************************************************************/
var µb = µBlock;
// fedcba9876543210
// ||| | | |
// ||| | | |
@ -1023,7 +1025,7 @@ FilterParser.prototype.parseOptParty = function(not) {
/******************************************************************************/
FilterParser.prototype.parseOptHostnames = function(raw) {
var µburi = µBlock.URI;
var µburi = µb.URI;
var hostnames = raw.split('|');
var hostname, not, domain;
for ( var i = 0; i < hostnames.length; i++ ) {
@ -1155,8 +1157,8 @@ FilterParser.prototype.parse = function(s) {
var FilterContainer = function() {
this.reAnyToken = /[%0-9a-z]+/g;
this.buckets = new Array(8);
this.blockedAnyPartyHostnames = new µBlock.LiquidDict();
this.blocked3rdPartyHostnames = new µBlock.LiquidDict();
this.blockedAnyPartyHostnames = new µb.LiquidDict();
this.blocked3rdPartyHostnames = new µb.LiquidDict();
this.filterParser = new FilterParser();
this.noDomainBits = this.toDomainBits(noDomainName);
this.reset();
@ -1633,7 +1635,7 @@ FilterContainer.prototype.matchAnyPartyHostname = function(requestHostname) {
return '||' + requestHostname + '^';
}
// Check parent hostnames if quick test failed
var hostnames = µBlock.URI.parentHostnamesFromHostname(requestHostname);
var hostnames = µb.URI.parentHostnamesFromHostname(requestHostname);
for ( var i = 0, n = hostnames.length; i < n; i++ ) {
if ( this.blockedAnyPartyHostnames.test(hostnames[i]) ) {
return '||' + hostnames[i] + '^';
@ -1658,7 +1660,7 @@ FilterContainer.prototype.match3rdPartyHostname = function(requestHostname) {
return '||' + requestHostname + '^$third-party';
}
// Check parent hostnames if quick test failed
var hostnames = µBlock.URI.parentHostnamesFromHostname(requestHostname);
var hostnames = µb.URI.parentHostnamesFromHostname(requestHostname);
for ( var i = 0, n = hostnames.length; i < n; i++ ) {
if ( this.blocked3rdPartyHostnames.test(hostnames[i]) ) {
return '||' + hostnames[i] + '^$third-party';
@ -1675,9 +1677,10 @@ FilterContainer.prototype.match3rdPartyHostname = function(requestHostname) {
// Some type of requests are exceptional, they need custom handling,
// not the generic handling.
FilterContainer.prototype.matchStringExactType = function(pageDetails, requestURL, requestType, requestHostname) {
FilterContainer.prototype.matchStringExactType = function(pageDetails, requestURL, requestType) {
var url = requestURL.toLowerCase();
var pageDomain = pageDetails.pageDomain || '';
var requestHostname = µb.URI.hostnameFromURI(requestURL);
var party = requestHostname.slice(-pageDomain.length) === pageDomain ?
FirstParty :
ThirdParty;
@ -1710,7 +1713,7 @@ FilterContainer.prototype.matchStringExactType = function(pageDetails, requestUR
buckets[7] = categories[this.makeCategoryKey(BlockOneParty | type | this.noDomainBits)];
bf = this.matchTokens(url);
if ( bf === false ) {
return false;
return '';
}
// Test against allow filters
@ -1728,7 +1731,7 @@ FilterContainer.prototype.matchStringExactType = function(pageDetails, requestUR
/******************************************************************************/
FilterContainer.prototype.matchString = function(pageDetails, requestURL, requestType, requestHostname) {
FilterContainer.prototype.matchString = function(pageDetails, requestURL, requestType) {
// https://github.com/gorhill/httpswitchboard/issues/239
// Convert url to lower case:
// `match-case` option not supported, but then, I saw only one
@ -1757,9 +1760,17 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
// filter.
var pageDomain = pageDetails.pageDomain || '';
var party = requestHostname.slice(-pageDomain.length) === pageDomain ?
FirstParty :
ThirdParty;
var requestHostname = µb.URI.hostnameFromURI(requestURL);
// Find out the relation between the page and request
var party = ThirdParty;
if ( requestHostname.slice(0 - pageDomain.length) === pageDomain ) {
// Be sure to not confuse 'example.com' with 'anotherexample.com'
var c = requestHostname.charAt(0 - pageDomain.length - 1);
if ( c === '' || c === '.' ) {
party = FirstParty;
}
}
// This will be used by hostname-based filters
pageHostname = pageDetails.pageHostname || '';
@ -1784,7 +1795,7 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
buckets[7] = categories[this.makeCategoryKey(BlockOneParty | Important | type | this.noDomainBits)];
var bf = this.matchTokens(url);
if ( bf !== false ) {
return bf.toString();
return bf.toString() + '$important';
}
// Test hostname-based block filters
@ -1810,7 +1821,7 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
// If there is no block filter, no need to test against allow filters
if ( bf === false ) {
return false;
return '';
}
// Test against allow filters

View File

@ -38,32 +38,180 @@ To create a log of net requests
/******************************************************************************/
var µb = µBlock;
var frameStoreJunkyard = [];
var pageStoreJunkyard = [];
/******************************************************************************/
/******************************************************************************/
// To mitigate memory churning
var netFilteringResultCacheEntryJunkyard = [];
var netFilteringResultCacheEntryJunkyardMax = 200;
/******************************************************************************/
var frameStoreFactory = function(frameURL) {
var entry = frameStoreJunkyard.pop();
if ( entry ) {
return entry.init(frameURL);
}
return new FrameStore(frameURL);
var NetFilteringResultCacheEntry = function(data) {
this.init(data);
};
var disposeFrameStores = function(map) {
for ( var k in map ) {
if ( map.hasOwnProperty(k) === false ) {
/******************************************************************************/
NetFilteringResultCacheEntry.prototype.init = function(data) {
this.data = data;
this.time = Date.now();
};
/******************************************************************************/
NetFilteringResultCacheEntry.prototype.dispose = function() {
this.data = null;
if ( netFilteringResultCacheEntryJunkyard.length < netFilteringResultCacheEntryJunkyardMax ) {
netFilteringResultCacheEntryJunkyard.push(this);
}
};
/******************************************************************************/
NetFilteringResultCacheEntry.factory = function(data) {
var entry = netFilteringResultCacheEntryJunkyard.pop();
if ( entry === undefined ) {
entry = new NetFilteringResultCacheEntry(data);
} else {
entry.init(data);
}
return entry;
};
/******************************************************************************/
/******************************************************************************/
// To mitigate memory churning
var uidGenerator = 1;
var netFilteringCacheJunkyard = [];
var netFilteringCacheJunkyardMax = 10;
/******************************************************************************/
var NetFilteringResultCache = function() {
this.init();
};
/******************************************************************************/
NetFilteringResultCache.factory = function() {
var entry = netFilteringCacheJunkyard.pop();
if ( entry === undefined ) {
entry = new NetFilteringResultCache();
} else {
entry.init();
}
return entry;
};
/******************************************************************************/
NetFilteringResultCache.prototype.init = function() {
this.uname = 'NetFilteringResultCache:' + uidGenerator++;
this.urls = {};
this.count = 0;
this.shelfLife = 60 * 1000;
};
/******************************************************************************/
NetFilteringResultCache.prototype.dispose = function() {
for ( var key in this.urls ) {
if ( this.urls.hasOwnProperty(key) === false ) {
continue;
}
if ( frameStoreJunkyard.length > 50 ) {
this.urls[key].dispose();
}
µBlock.asyncJobs.remove(this.uname);
this.uname = '';
this.urls = {};
this.count = 0;
if ( netFilteringCacheJunkyard.length < netFilteringCacheJunkyardMax ) {
netFilteringCacheJunkyard.push(this);
}
return null;
};
/******************************************************************************/
NetFilteringResultCache.prototype.add = function(url, data) {
var entry = this.urls[url];
if ( entry !== undefined ) {
entry.data = data;
entry.time = Date.now();
return;
}
this.urls[url] = NetFilteringResultCacheEntry.factory(data);
if ( this.count === 0 ) {
this.pruneAsync();
}
this.count += 1;
};
/******************************************************************************/
NetFilteringResultCache.prototype.fetchAll = function() {
return this.urls;
};
/******************************************************************************/
NetFilteringResultCache.prototype.compareEntries = function(a, b) {
return this.urls[b].time - this.urls[a].time;
};
/******************************************************************************/
NetFilteringResultCache.prototype.prune = function() {
var keys = Object.keys(this.urls).sort(this.compareEntries.bind(this));
var obsolete = Date.now() - this.shelfLife;
var key, entry;
var i = keys.length;
while ( i-- ) {
key = keys[i];
entry = this.urls[key];
if ( entry.time > obsolete ) {
break;
}
frameStoreJunkyard.push(map[k].dispose());
entry.dispose();
delete this.urls[key];
}
this.count -= keys.length - i - 1;
if ( this.count > 0 ) {
this.pruneAsync();
}
return {};
};
// https://www.youtube.com/watch?v=0vTBZzB_gfY
/******************************************************************************/
NetFilteringResultCache.prototype.pruneAsync = function() {
µBlock.asyncJobs.add(
this.uname,
null,
this.prune.bind(this),
this.shelfLife + 120000,
false
);
};
/******************************************************************************/
NetFilteringResultCache.prototype.lookup = function(url) {
var entry = this.urls[url];
return entry !== undefined ? entry.data : undefined;
};
/******************************************************************************/
/******************************************************************************/
// To mitigate memory churning
var frameStoreJunkyard = [];
var frameStoreJunkyardMax = 50;
/******************************************************************************/
var FrameStore = function(frameURL) {
@ -72,6 +220,18 @@ var FrameStore = function(frameURL) {
/******************************************************************************/
FrameStore.factory = function(frameURL) {
var entry = frameStoreJunkyard.pop();
if ( entry === undefined ) {
entry = new FrameStore(frameURL);
} else {
entry.init(frameURL);
}
return entry;
};
/******************************************************************************/
FrameStore.prototype.init = function(frameURL) {
var µburi = µb.URI;
this.pageHostname = µburi.hostnameFromURI(frameURL);
@ -83,18 +243,24 @@ FrameStore.prototype.init = function(frameURL) {
FrameStore.prototype.dispose = function() {
this.pageHostname = this.pageDomain = '';
return this;
if ( frameStoreJunkyard.length < frameStoreJunkyardMax ) {
frameStoreJunkyard.push(this);
}
return null;
};
/******************************************************************************/
/******************************************************************************/
var pageStoreFactory = function(tabId, pageURL) {
var entry = pageStoreJunkyard.pop();
if ( entry ) {
return entry.init(tabId, pageURL);
}
return new PageStore(tabId, pageURL);
};
// To mitigate memory churning
var pageStoreJunkyard = [];
var pageStoreJunkyardMax = 10;
/******************************************************************************/
// Cache only what is worth it if logging is disabled
// http://jsperf.com/string-indexof-vs-object
var collapsibleRequestTypes = 'image sub_frame object';
/******************************************************************************/
@ -104,6 +270,18 @@ var PageStore = function(tabId, pageURL) {
/******************************************************************************/
PageStore.factory = function(tabId, pageURL) {
var entry = pageStoreJunkyard.pop();
if ( entry === undefined ) {
entry = new PageStore(tabId, pageURL);
} else {
entry.init(tabId, pageURL);
}
return entry;
};
/******************************************************************************/
PageStore.prototype.init = function(tabId, pageURL) {
this.tabId = tabId;
this.previousPageURL = '';
@ -114,26 +292,33 @@ PageStore.prototype.init = function(tabId, pageURL) {
// Use hostname if no domain can be extracted
this.pageDomain = µb.URI.domainFromHostname(this.pageHostname) || this.pageHostname;
this.frames = disposeFrameStores(this.frames);
this.frames = {};
this.netFiltering = true;
this.netFilteringReadTime = 0;
this.perLoadBlockedRequestCount = 0;
this.perLoadAllowedRequestCount = 0;
this.blockedRequests = {};
this.allowedRequests = {};
this.disposeTime = 0;
this.netFilteringCache = NetFilteringResultCache.factory();
if ( µb.userSettings.logRequests ) {
this.netFilteringCache.shelfLife = 30 * 60 * 1000;
}
return this;
};
/******************************************************************************/
PageStore.prototype.reuse = function(pageURL) {
this.disposeFrameStores();
this.netFilteringCache = this.netFilteringCache.dispose();
var previousPageURL = this.pageURL;
this.init(this.tabId, pageURL);
this.previousPageURL = previousPageURL;
return this;
};
// https://www.youtube.com/watch?v=dltNSbOupgE
/******************************************************************************/
PageStore.prototype.dispose = function() {
@ -142,11 +327,30 @@ PageStore.prototype.dispose = function() {
// sizeable enough chunks (especially requests, through the request URL
// used as a key).
this.pageURL = '';
this.previousPageURL = '';
this.pageHostname = '';
this.pageDomain = '';
if ( pageStoreJunkyard.length < 8 ) {
this.disposeFrameStores();
this.netFilteringCache = this.netFilteringCache.dispose();
if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) {
pageStoreJunkyard.push(this);
}
return null;
};
/******************************************************************************/
PageStore.prototype.disposeFrameStores = function() {
var frames = this.frames;
if ( typeof frames === 'object' ) {
for ( var k in frames ) {
if ( frames.hasOwnProperty(k) === false ) {
continue;
}
frames[k].dispose();
}
}
this.frames = {};
};
/******************************************************************************/
@ -154,7 +358,7 @@ PageStore.prototype.dispose = function() {
PageStore.prototype.addFrame = function(frameId, frameURL) {
var frameStore = this.frames[frameId];
if ( frameStore === undefined ) {
this.frames[frameId] = frameStore = frameStoreFactory(frameURL);
this.frames[frameId] = frameStore = FrameStore.factory(frameURL);
//console.debug('µBlock> PageStore.addFrame(%d, "%s")', frameId, frameURL);
}
return frameStore;
@ -178,35 +382,26 @@ PageStore.prototype.getNetFilteringSwitch = function() {
/******************************************************************************/
PageStore.prototype.recordRequest = function(type, url, reason) {
var blocked = reason !== false && reason.slice(0, 2) !== '@@';
if ( !blocked ) {
this.perLoadAllowedRequestCount++;
µb.localSettings.allowedRequestCount++;
if ( µb.userSettings.logAllowedRequests ) {
this.allowedRequests[url] = type + '\t' + (reason || '');
}
return;
PageStore.prototype.filterRequest = function(context, requestType, requestURL) {
var result = this.netFilteringCache.lookup(requestURL);
if ( result !== undefined ) {
return result.slice(result.indexOf('\t') + 1);
}
µb.updateBadgeAsync(this.tabId);
this.perLoadBlockedRequestCount++;
µb.localSettings.blockedRequestCount++;
// https://github.com/gorhill/uBlock/issues/7
// https://github.com/gorhill/uBlock/issues/12
// No need to record blocked requests which are not image or frame, as
// these are the only ones we try to hide when they are blocked.
if ( µb.userSettings.logBlockedRequests === false ) {
if ( type === 'image' || type === 'sub_frame' ) {
this.blockedRequests[url] = true;
}
return;
//console.debug('µBlock> PageStore.filterRequest(): "%s" not in cache', requestURL);
result = µb.netFilteringEngine.matchString(context, requestURL, requestType);
if ( collapsibleRequestTypes.indexOf(requestType) !== -1 || µb.userSettings.logRequests ) {
this.netFilteringCache.add(requestURL, requestType + '\t' + result);
}
this.blockedRequests[url] = type + '\t' + reason;
return result;
};
/******************************************************************************/
// false: not blocked
// true: blocked
PageStore.prototype.boolFromResult = function(result) {
return typeof result === 'string' && result !== '' && result.slice(0, 2) !== '@@';
};
/******************************************************************************/
@ -229,7 +424,7 @@ PageStore.prototype.updateBadge = function() {
/******************************************************************************/
return {
factory: pageStoreFactory
factory: PageStore.factory
};
})();

View File

@ -59,7 +59,7 @@ var renderStats = function() {
// - logging of requests enabled
uDom('#gotoLog').toggleClass(
'enabled',
isHTTP && (stats.logBlockedRequests || stats.logAllowedRequests)
isHTTP && stats.logRequests
);
// Conditions for element picker:

View File

@ -31,27 +31,13 @@ messaging.start('stats.js');
/******************************************************************************/
var logBlockedSettingChanged = function() {
var logSettingChanged = function() {
messaging.tell({
what: 'userSettings',
name: 'logBlockedRequests',
name: 'logRequests',
value: this.checked
});
uDom('#requests table').toggleClass('hideBlocked', !this.checked);
uDom('#requests').toggleClass('logEnabled', this.checked || uDom('#logAllowedRequests').prop('checked'));
renderPageSelector();
};
/******************************************************************************/
var logAllowedSettingChanged = function() {
messaging.tell({
what: 'userSettings',
name: 'logAllowedRequests',
value: this.checked
});
uDom('#requests table').toggleClass('hideAllowed', !this.checked);
uDom('#requests').toggleClass('logEnabled', this.checked || uDom('#logBlockedRequests').prop('checked'));
uDom('#requests').toggleClass('logEnabled', this.checked);
renderPageSelector();
};
@ -186,7 +172,7 @@ var pageSelectorChanged = function() {
/******************************************************************************/
var renderPageSelector = function(targetTabId) {
if ( !uDom('#logBlockedRequests').prop('checked') && !uDom('#logAllowedRequests').prop('checked') ) {
if ( !uDom('#logRequests').prop('checked') ) {
return;
}
var selectedTabId = targetTabId || parseInt(uDom('#pageSelector').val(), 10);
@ -228,18 +214,14 @@ var renderPageSelector = function(targetTabId) {
/******************************************************************************/
var onUserSettingsReceived = function(details) {
uDom('#logBlockedRequests').prop('checked', details.logBlockedRequests);
uDom('#logAllowedRequests').prop('checked', details.logAllowedRequests);
uDom('#requests').toggleClass('logEnabled', details.logBlockedRequests || details.logAllowedRequests);
uDom('#requests table').toggleClass('hideBlocked', !details.logBlockedRequests);
uDom('#requests table').toggleClass('hideAllowed', !details.logAllowedRequests);
uDom('#logRequests').prop('checked', details.logRequests);
uDom('#requests').toggleClass('logEnabled', details.logRequests);
var matches = window.location.search.slice(1).match(/(?:^|&)which=(\d+)/);
var tabId = matches && matches.length === 2 ? parseInt(matches[1], 10) : 0;
renderPageSelector(tabId);
uDom('#logBlockedRequests').on('change', logBlockedSettingChanged);
uDom('#logAllowedRequests').on('change', logAllowedSettingChanged);
uDom('#logRequests').on('change', logSettingChanged);
uDom('#refresh').on('click', function() { renderPageSelector(); });
uDom('#pageSelector').on('change', pageSelectorChanged);
};

View File

@ -114,7 +114,11 @@
µBlock.unbindTabFromPageStats = function(tabId) {
//console.debug('µBlock> unbindTabFromPageStats(%d)', tabId);
delete this.pageStores[tabId];
var pageStore = this.pageStores[tabId];
if ( pageStore !== undefined ) {
pageStore.dispose();
delete this.pageStores[tabId];
}
};
/******************************************************************************/

View File

@ -51,7 +51,6 @@ var onBeforeRequest = function(details) {
}
var µburi = µb.URI.set(requestURL);
var requestHostname = µburi.hostname;
var requestPath = µburi.path;
// rhill 2013-12-15:
@ -78,31 +77,35 @@ var onBeforeRequest = function(details) {
}
}
var reason = false;
var result = '';
if ( pageStore.getNetFilteringSwitch() ) {
reason = µb.netFilteringEngine.matchString(requestContext, requestURL, requestType, requestHostname);
result = pageStore.filterRequest(requestContext, requestType, requestURL);
}
// Record what happened.
pageStore.recordRequest(requestType, requestURL, reason);
// Not blocked?
if ( reason === false || reason.slice(0, 2) === '@@' ) {
//console.debug('µBlock> onBeforeRequest()> ALLOW "%s" (%o)', details.url, details);
// Not blocked
if ( pageStore.boolFromResult(result) === false ) {
pageStore.perLoadAllowedRequestCount++;
µb.localSettings.allowedRequestCount++;
// https://github.com/gorhill/uBlock/issues/114
if ( frameId > 0 && frameStore === undefined ) {
pageStore.addFrame(frameId, requestURL);
}
//console.debug('µBlock> onBeforeRequest()> ALLOW "%s" (%o)', details.url, details);
return;
}
// Blocked
//console.debug('µBlock> onBeforeRequest()> BLOCK "%s" (%o) because "%s"', details.url, details, reason);
pageStore.perLoadBlockedRequestCount++;
µb.localSettings.blockedRequestCount++;
µb.updateBadgeAsync(tabId);
// https://github.com/gorhill/uBlock/issues/18
// Do not use redirection, we need to block outright to be sure the request
// will not be made. There can be no such guarantee with redirection.
//console.debug('µBlock> onBeforeRequest()> BLOCK "%s" (%o) because "%s"', details.url, details, result);
return { 'cancel': true };
};
@ -166,18 +169,13 @@ var onBeforeSendHeaders = function(details) {
// switch of the popup. If so, that would require being able to lookup
// a page store from a URL. Have to keep in mind the same URL can appear
// in multiple tabs.
var reason = false;
var result = '';
if ( pageStore.getNetFilteringSwitch() ) {
reason = µb.netFilteringEngine.matchStringExactType(
pageDetails,
requestURL,
'popup',
µburi.hostnameFromURI(requestURL)
);
result = µb.netFilteringEngine.matchStringExactType(pageDetails, requestURL, 'popup');
}
// Not blocked?
if ( reason === false || reason.slice(0, 2) === '@@' ) {
if ( result === '' || result.slice(0, 2) === '@@' ) {
return;
}

View File

@ -70,12 +70,6 @@ tr.requestEntry td:nth-of-type(1) {
tr.requestEntry td:nth-of-type(2) {
text-align: right;
}
table.hideBlocked tr.logBlocked {
display: none;
}
table.hideAllowed tr.logAllowed {
display: none;
}
tr.logBlocked {
background-color: #fff8f8;
}
@ -98,12 +92,9 @@ tr.logAllowed ~ tr td:nth-of-type(3) b {
<body>
<ul>
<li><input id="logBlockedRequests" type="checkbox" data-range="bool" /><label data-i18n="logBlockedRequestsPrompt"></label>
<li><input id="logRequests" type="checkbox" data-range="bool" /><label data-i18n="logNetRequestsPrompt"></label>
<button class="whatisthis"></button>
<div class="whatisthis-expandable para" data-i18n="logBlockedRequestsHelp"></div>
<li><input id="logAllowedRequests" type="checkbox" data-range="bool" /><label data-i18n="logAllowedRequestsPrompt"></label>
<button class="whatisthis"></button>
<div class="whatisthis-expandable para" data-i18n="logAllowedRequestsHelp"></div>
<div class="whatisthis-expandable para" data-i18n="logNetRequestsHelp"></div>
</ul>
<div id="requests">