this fixes #5

This commit is contained in:
gorhill 2014-08-02 11:40:27 -04:00
parent cc69d0c2d4
commit bee017034a
12 changed files with 223 additions and 153 deletions

View file

@ -612,10 +612,7 @@ FilterContainer.prototype.addFilterEntry = function(filterDict, hash, f) {
/******************************************************************************/
FilterContainer.prototype.retrieveGenericSelectors = function(tabHostname, request) {
if ( !tabHostname || µb.getCosmeticFilteringSwitch(tabHostname) !== true ) {
return;
}
FilterContainer.prototype.retrieveGenericSelectors = function(request) {
if ( µb.userSettings.parseAllABPHideFilters !== true ) {
return;
}
@ -670,10 +667,7 @@ FilterContainer.prototype.retrieveGenericSelectors = function(tabHostname, reque
/******************************************************************************/
FilterContainer.prototype.retrieveDomainSelectors = function(tabHostname, request) {
if ( !tabHostname || µb.getCosmeticFilteringSwitch(tabHostname) !== true ) {
return;
}
FilterContainer.prototype.retrieveDomainSelectors = function(request) {
if ( µb.userSettings.parseAllABPHideFilters !== true ) {
return;
}

View file

@ -36,7 +36,8 @@ return {
logBlockedRequests: false,
logAllowedRequests: false,
parseAllABPHideFilters: true,
netExceptionList: {},
netExceptionList: {}, // TODO: remove once all users are up to date
netWhitelist: '',
showIconBadge: true
},
localSettings: {
@ -70,6 +71,8 @@ return {
remoteBlacklists: {
},
netWhitelist: {},
netWhitelistModifyTime: 0,
pageStores: {},
storageQuota: chrome.storage.local.QUOTA_BYTES,

View file

@ -420,6 +420,7 @@ var messaging = (function(name){
});
})();
/******************************************************************************/
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/7
@ -459,13 +460,19 @@ var messaging = (function(name){
// - Elements which resource URL changes
var onResourceLoaded = function(ev) {
var target = ev.target;
if ( !target || !target.src ) {
return;
}
var tag = target.tagName.toLowerCase();
if ( tag !== 'img' && tag !== 'iframe' ) {
return;
}
if ( !target || !target.src ) { return; }
if ( target.tagName.toLowerCase() !== 'iframe' ) { return; }
var onAnswerReceived = function(details) {
if ( details.blocked ) {
hideOne(target, details.collapse);
}
};
messaging.ask({ what: 'blockedRequest', url: target.src }, onAnswerReceived);
};
var onResourceFailed = function(ev) {
var target = ev.target;
if ( !target || !target.src ) { return; }
if ( target.tagName.toLowerCase() !== 'img' ) { return; }
var onAnswerReceived = function(details) {
if ( details.blocked ) {
hideOne(target, details.collapse);
@ -474,7 +481,7 @@ var messaging = (function(name){
messaging.ask({ what: 'blockedRequest', url: target.src }, onAnswerReceived);
};
document.addEventListener('load', onResourceLoaded, true);
document.addEventListener('error', onResourceLoaded, true);
document.addEventListener('error', onResourceFailed, true);
})();
/******************************************************************************/

View file

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
/* global chrome, µBlock */
/* global chrome, µBlock, YaMD5 */
/******************************************************************************/
@ -49,7 +49,7 @@ var getStats = function(request) {
r.pageHostname = pageStore.pageHostname;
r.pageBlockedRequestCount = pageStore.perLoadBlockedRequestCount;
r.pageAllowedRequestCount = pageStore.perLoadAllowedRequestCount;
r.netFilteringSwitch = µb.getNetFilteringSwitch(pageStore.pageHostname);
r.netFilteringSwitch = pageStore.getNetFilteringSwitch();
}
return r;
};
@ -73,7 +73,8 @@ var onMessage = function(request, sender, callback) {
case 'toggleNetFiltering':
µBlock.toggleNetFilteringSwitch(
request.hostname,
request.url,
request.scope,
request.state
);
µBlock.updateBadgeAsync(request.tabId);
@ -100,6 +101,8 @@ var onMessage = function(request, sender, callback) {
(function() {
var µb = µBlock;
var onMessage = function(request, sender, callback) {
// Async
switch ( request.what ) {
@ -112,23 +115,24 @@ var onMessage = function(request, sender, callback) {
var pageStore;
if ( sender && sender.tab ) {
pageStore = µBlock.pageStoreFromTabId(sender.tab.id);
pageStore = µb.pageStoreFromTabId(sender.tab.id);
}
var tabHostname = pageStore ? pageStore.pageHostname : '';
switch ( request.what ) {
case 'retrieveDomainCosmeticSelectors':
response = µBlock.abpHideFilters.retrieveDomainSelectors(tabHostname, request);
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
response = µb.abpHideFilters.retrieveDomainSelectors(request);
}
break;
default:
return µBlock.messaging.defaultHandler(request, sender, callback);
return µb.messaging.defaultHandler(request, sender, callback);
}
callback(response);
};
µBlock.messaging.listen('contentscript-start.js', onMessage);
µb.messaging.listen('contentscript-start.js', onMessage);
})();
@ -138,6 +142,8 @@ var onMessage = function(request, sender, callback) {
(function() {
var µb = µBlock;
var onMessage = function(request, sender, callback) {
// Async
switch ( request.what ) {
@ -150,18 +156,19 @@ var onMessage = function(request, sender, callback) {
var pageStore;
if ( sender && sender.tab ) {
pageStore = µBlock.pageStoreFromTabId(sender.tab.id);
pageStore = µb.pageStoreFromTabId(sender.tab.id);
}
var tabHostname = pageStore ? pageStore.pageHostname : '';
switch ( request.what ) {
case 'retrieveGenericCosmeticSelectors':
response = µBlock.abpHideFilters.retrieveGenericSelectors(tabHostname, request);
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
response = µb.abpHideFilters.retrieveGenericSelectors(request);
}
break;
case 'blockedRequests':
response = {
collapse: µBlock.userSettings.collapseBlocked,
collapse: µb.userSettings.collapseBlocked,
blockedRequests: pageStore ? pageStore.blockedRequests : {}
};
break;
@ -169,19 +176,19 @@ var onMessage = function(request, sender, callback) {
// Check a single request
case 'blockedRequest':
response = {
collapse: µBlock.userSettings.collapseBlocked,
collapse: µb.userSettings.collapseBlocked,
blocked: pageStore && pageStore.blockedRequests[request.url]
};
break;
default:
return µBlock.messaging.defaultHandler(request, sender, callback);
return µb.messaging.defaultHandler(request, sender, callback);
}
callback(response);
};
µBlock.messaging.listen('contentscript-end.js', onMessage);
µb.messaging.listen('contentscript-end.js', onMessage);
})();
@ -352,11 +359,11 @@ var onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'getWhitelist':
response = µb.userSettings.netExceptionList;
response = µb.stringFromWhitelist(µb.netWhitelist);
break;
case 'setWhitelist':
µb.userSettings.netExceptionList = request.whitelist;
µb.netWhitelist = µb.whitelistFromString(request.whitelist);
µb.saveWhitelist();
break;

View file

@ -111,6 +111,8 @@ PageStore.prototype.init = function(tabId, pageURL) {
this.pageHostname = µb.URI.hostnameFromURI(pageURL);
this.pageDomain = µb.URI.domainFromHostname(this.pageHostname);
this.frames = disposeFrameStores(this.frames);
this.netFiltering = true;
this.netFilteringReadTime = 0;
this.perLoadBlockedRequestCount = 0;
this.perLoadAllowedRequestCount = 0;
this.blockedRequests = {};
@ -122,16 +124,9 @@ PageStore.prototype.init = function(tabId, pageURL) {
/******************************************************************************/
PageStore.prototype.reuse = function(pageURL) {
this.previousPageURL = this.pageURL;
this.pageURL = pageURL;
this.pageHostname = µb.URI.hostnameFromURI(pageURL);
this.pageDomain = µb.URI.domainFromHostname(this.pageHostname);
this.frames = disposeFrameStores(this.frames);
this.perLoadBlockedRequestCount = 0;
this.perLoadAllowedRequestCount = 0;
this.blockedRequests = {};
this.allowedRequests = {};
this.disposeTime = 0;
var previousPageURL = this.pageURL;
this.init(this.tabId, pageURL);
this.previousPageURL = previousPageURL;
return this;
};
@ -169,6 +164,16 @@ PageStore.prototype.getFrame = function(frameId) {
/******************************************************************************/
PageStore.prototype.getNetFilteringSwitch = function() {
if ( this.netFilteringReadTime < µb.netWhitelistModifyTime ) {
this.netFiltering = µb.getNetFilteringSwitch(this.pageURL, this.pageDomain);
this.netFilteringReadTime = Date.now();
}
return this.netFiltering;
};
/******************************************************************************/
PageStore.prototype.recordRequest = function(type, url, reason) {
var blocked = reason !== false && reason.slice(0, 2) !== '@@';
@ -212,13 +217,13 @@ PageStore.prototype.updateBadge = function() {
// https://github.com/gorhill/uBlock/issues/19
// TODO: need to check with µb object to see whether tab still exists.
var netFilteringSwitch = µb.getNetFilteringSwitch(this.pageHostname);
var iconPath = netFilteringSwitch ? 'img/browsericons/icon19.png' : 'img/browsericons/icon19-off.png';
var netFiltering = this.getNetFilteringSwitch();
var iconPath = netFiltering ? 'img/browsericons/icon19.png' : 'img/browsericons/icon19-off.png';
chrome.browserAction.setIcon({ tabId: this.tabId, path: iconPath });
var iconStr = '';
if ( µb.userSettings.showIconBadge && netFilteringSwitch && this.perLoadBlockedRequestCount ) {
if ( µb.userSettings.showIconBadge && netFiltering && this.perLoadBlockedRequestCount ) {
iconStr = this.perLoadBlockedRequestCount.toLocaleString();
}
chrome.browserAction.setBadgeText({

View file

@ -134,14 +134,15 @@ chrome.tabs.query({ active: true, currentWindow: true }, onTabsReceived);
/******************************************************************************/
var handleNetFilteringSwitch = function() {
var toggleNetFilteringSwitch = function(ev) {
if ( !stats || !stats.pageURL ) {
return;
}
var off = uDom(this).toggleClass('off').hasClassName('off');
messaging.tell({
what: 'toggleNetFiltering',
hostname: stats.pageHostname,
url: stats.pageURL,
scope: ev.ctrlKey || ev.metaKey ? 'page' : '',
state: !off,
tabId: stats.tabId
});
@ -186,7 +187,7 @@ var renderHeader = function() {
var installEventHandlers = function() {
uDom('h1,h2,h3,h4').on('click', gotoDashboard);
uDom('#switch .fa').on('click', handleNetFilteringSwitch);
uDom('#switch .fa').on('click', toggleNetFilteringSwitch);
uDom('#gotoLog').on('click', gotoStats);
uDom('#gotoPick').on('click', gotoPick);
};

View file

@ -72,7 +72,22 @@
µBlock.loadUserSettings = function() {
var settingsLoaded = function(store) {
µBlock.userSettings = store;
var µb = µBlock;
// Backward compatibility after fix to #5
// TODO: remove once all users are up to date with latest version.
if ( store.netExceptionList ) {
if ( store.netWhitelist === '' ) {
store.netWhitelist = Object.keys(store.netExceptionList).join('\n');
if ( store.netWhitelist !== '' ) {
chrome.storage.local.set({ 'netWhitelist': store.netWhitelist });
}
}
delete store.netExceptionList;
chrome.storage.local.remove('netExceptionList');
}
µb.userSettings = store;
µb.netWhitelist = µb.whitelistFromString(store.netWhitelist);
µb.netWhitelistModifyTime = Date.now();
};
chrome.storage.local.get(this.userSettings, settingsLoaded);
@ -81,10 +96,12 @@
/******************************************************************************/
µBlock.saveWhitelist = function() {
var bin = { 'netExceptionList': this.userSettings.netExceptionList };
this.userSettings.netWhitelist = this.stringFromWhitelist(this.netWhitelist);
var bin = { 'netWhitelist': this.userSettings.netWhitelist };
chrome.storage.local.set(bin, function() {
µBlock.getBytesInUse();
});
this.netWhitelistModifyTime = Date.now();
};
/******************************************************************************/

View file

@ -26,13 +26,13 @@
(function(){
// When the DOM content of root frame is loaded, this means the tab
// content has changed.
function onDOMContentLoaded(details) {
function onNavigationCommitted(details) {
if ( details.frameId !== 0 ) {
return;
}
µBlock.updateBadgeAsync(details.tabId);
µBlock.bindTabToPageStats(details.tabId, details.url);
}
chrome.webNavigation.onDOMContentLoaded.addListener(onDOMContentLoaded);
chrome.webNavigation.onCommitted.addListener(onNavigationCommitted);
// It may happen the URL in the tab changes, while the page's document
// stays the same (for instance, Google Maps). Without this listener,
@ -41,15 +41,10 @@
if ( !tab.url || tab.url === '' ) {
return;
}
var µb = µBlock;
if ( !changeInfo.url || !µb.pageStores[tabId] ) {
if ( !changeInfo.url ) {
return;
}
// If URL is unsupported scheme, unbind tab
if ( changeInfo.url && changeInfo.url.slice(0, 4) !== 'http' ) {
µb.unbindTabFromPageStats(tabId);
}
µb.updateBadgeAsync(tabId);
µBlock.bindTabToPageStats(tabId, changeInfo.url);
}
chrome.tabs.onUpdated.addListener(onTabUpdated);
@ -95,7 +90,7 @@
// Create an entry for the tab if it doesn't exist.
µBlock.bindTabToPageStats = function(tabId, pageURL) {
µBlock.bindTabToPageStats = function(tabId, pageURL, context) {
this.updateBadgeAsync(tabId);
// https://github.com/gorhill/httpswitchboard/issues/303
@ -108,18 +103,17 @@
return null;
}
//console.debug('µBlock> bindTabToPageStats(%d, "%s")', tabId, pageURL);
// Reuse page store if one exists: this allows to guess if a tab is
// a popup.
var pageStore = this.pageStores[tabId];
if ( pageStore ) {
pageStore.reuse(pageURL);
} else {
pageStore = this.PageStore.factory(tabId, pageURL);
}
this.pageStores[tabId] = pageStore;
if ( pageStore ) {
if ( pageURL !== pageStore.pageURL || context === 'beforeRequest' ) {
pageStore.reuse(pageURL);
}
} else {
pageStore = this.pageStores[tabId] = this.PageStore.factory(tabId, pageURL);
}
return pageStore;
};

View file

@ -46,7 +46,7 @@ var onBeforeRequest = function(details) {
// Special handling for root document.
if ( requestType === 'main_frame' && details.parentFrameId === -1 ) {
µb.bindTabToPageStats(tabId, requestURL);
µb.bindTabToPageStats(tabId, requestURL, 'beforeRequest');
return;
}
@ -79,7 +79,7 @@ var onBeforeRequest = function(details) {
//}
var reason = false;
if ( µb.getNetFilteringSwitch(pageStore.pageHostname) ) {
if ( pageStore.getNetFilteringSwitch() ) {
reason = µb.abpFilters.matchString(requestContext, requestURL, requestType, requestHostname);
}
// Record what happened.
@ -167,7 +167,7 @@ var onBeforeSendHeaders = function(details) {
// a page store from a URL. Have to keep in mind the same URL can appear
// in multiple tabs.
var reason = false;
if ( µb.getNetFilteringSwitch(pageStore.pageHostname) ) {
if ( pageStore.getNetFilteringSwitch() ) {
reason = µb.abpFilters.matchStringExactType(
pageDetails,
requestURL,

View file

@ -19,18 +19,43 @@
Home: https://github.com/gorhill/uBlock
*/
/* global chrome, µBlock */
/* global µBlock */
/******************************************************************************/
µBlock.getNetFilteringSwitch = function(hostname) {
var netExceptionList = this.userSettings.netExceptionList;
if ( netExceptionList[hostname] !== undefined ) {
µBlock.getNetFilteringSwitch = function(url, domain) {
var keyHostname = this.URI.hostnameFromURI(url);
var pos = url.indexOf('#');
var keyURL = pos !== -1 ? url.slice(0, pos) : url;
// The caller may provide an already known domain -- convenient to reduce
// overhead of extracting a domain from the url
if ( domain === undefined ) {
domain = this.URI.domainFromHostname(keyHostname);
}
if ( !domain ) {
return false;
}
var hostnames = this.URI.parentHostnamesFromHostname(hostname);
while ( hostname = hostnames.shift() ) {
if ( netExceptionList[hostname] !== undefined ) {
var exceptions = this.netWhitelist[domain];
if ( !exceptions ) {
return true;
}
var i = exceptions.length;
var exception;
while ( i-- ) {
exception = exceptions[i];
if ( exception.indexOf('/') !== -1 ) {
if ( exception.slice(-1) === '*' ) {
exception = exception.slice(0, -1);
if ( keyURL.slice(0, exception.length) === exception ) {
return false;
}
} else if ( keyURL === exception ) {
return false;
}
} else if ( keyHostname.slice(-exception.length) === exception ) {
return false;
}
}
@ -39,58 +64,112 @@
/******************************************************************************/
µBlock.toggleNetFilteringSwitch = function(hostname, newState) {
var currentState = this.getNetFilteringSwitch(hostname);
µBlock.toggleNetFilteringSwitch = function(url, scope, newState) {
var keyHostname = this.URI.hostnameFromURI(url);
var pos = url.indexOf('#');
var keyURL = pos !== -1 ? url.slice(0, pos) : url;
var key = scope === 'page' ? keyURL : keyHostname;
// The caller may provide an already known domain -- convenient to reduce
// overhead of extracting a domain from `key`
var domain = this.URI.domainFromHostname(keyHostname);
if ( !domain ) {
return false;
}
var currentState = this.getNetFilteringSwitch(url, domain);
if ( newState === undefined ) {
newState = !currentState;
}
if ( newState === currentState ) {
return currentState;
}
var netExceptionList = this.userSettings.netExceptionList;
var netWhitelist = this.netWhitelist;
var exceptions = netWhitelist[domain];
if ( !exceptions ) {
exceptions = netWhitelist[domain] = [];
}
// Add to exception list
if ( !newState ) {
netExceptionList[hostname] = true;
this.saveExceptionList();
exceptions.push(key);
this.saveWhitelist();
return true;
}
// Remove from exception list
var hostnames = this.URI.allHostnamesFromHostname(hostname);
while ( hostname = hostnames.shift() ) {
if ( netExceptionList[hostname] !== undefined ) {
delete netExceptionList[hostname];
var i = exceptions.length;
var exception;
while ( i-- ) {
exception = exceptions[i];
if ( exception.indexOf('/') !== -1 ) {
if ( exception.slice(-1) === '*' ) {
exception = exception.slice(0, -1);
if ( keyURL.slice(0, exception.length) === exception ) {
exceptions.splice(i, 1);
}
} else if ( keyURL === exception ) {
exceptions.splice(i, 1);
}
} else if ( keyHostname.slice(-exception.length) === exception ) {
exceptions.splice(i, 1);
}
}
this.saveExceptionList();
return false;
if ( exceptions.length === 0 ) {
delete netWhitelist[domain];
}
this.saveWhitelist();
return true;
};
/******************************************************************************/
// For now we will use the net exception list
µBlock.getCosmeticFilteringSwitch = function(hostname) {
var netExceptionList = this.userSettings.netExceptionList;
if ( netExceptionList[hostname] !== undefined ) {
return false;
}
var hostnames = this.URI.parentHostnamesFromHostname(hostname);
while ( hostname = hostnames.shift() ) {
if ( netExceptionList[hostname] !== undefined ) {
return false;
}
}
return true;
µBlock.getCosmeticFilteringSwitch = function(url, domain) {
return this.getNetFilteringSwitch(url, domain);
};
/******************************************************************************/
µBlock.saveExceptionList = function() {
chrome.storage.local.set({
'netExceptionList': this.userSettings.netExceptionList
});
µBlock.stringFromWhitelist = function(exceptions) {
var r = {};
var i, bucket;
for ( var domain in exceptions ) {
if ( exceptions.hasOwnProperty(domain) === false ) {
continue;
}
bucket = exceptions[domain];
for ( i = 0; i < bucket.length; i++ ) {
r[bucket[i]] = true;
}
}
return Object.keys(r).sort(function(a,b){return a.localeCompare(b);}).join('\n');
};
/******************************************************************************/
µBlock.whitelistFromString = function(s) {
var exceptions = {};
var lines = s.split(/[\n\r]+/);
var line, domain, bucket;
for ( var i = 0; i < lines.length; i++ ) {
line = lines[i].trim();
domain = line.indexOf('/') !== -1 ?
this.URI.domainFromURI(line) :
this.URI.domainFromHostname(line);
if ( !domain ) {
continue;
}
bucket = exceptions[domain];
if ( bucket === undefined ) {
exceptions[domain] = [line];
} else {
bucket.push(line);
}
}
return exceptions;
};
/******************************************************************************/

View file

@ -34,62 +34,25 @@ messaging.start('whitelist.js');
var cachedWhitelist = '';
// Could make it more fancy if needed. But speed... It's a compromise.
var reBadHostname = /[\x00-\x08\x0b\x0c\x0e-\x1f\x21-\x2c\x2f\x3a-\x40\x5b-\x60\x7b-\x7e]/;
/******************************************************************************/
var validateHostname = function(s) {
var hn = punycode.toASCII(s).toLowerCase();
if ( reBadHostname.test(hn) ) {
return '';
}
return hn;
};
var whitelistFromString = function(s) {
var whitelist = {};
var items = s.split(/\s+/);
var item;
for ( var i = 0; i < items.length; i++ ) {
item = validateHostname(items[i]);
if ( item !== '' ) {
whitelist[item] = true;
}
}
return whitelist;
};
var stringFromWhitelist = function(whitelist) {
var s = [];
var items = Object.keys(whitelist);
for ( var i = 0; i < items.length; i++ ) {
s.push(punycode.toUnicode(items[i]));
}
return s.sort(function(a, b) { return a.localeCompare(b); }).join('\n');
};
/******************************************************************************/
var badWhitelist = function(s) {
return reBadHostname.test(s);
};
var reUnwantedChars = /[\x00-\x09\x0b\x0c\x0e-\x1f!"#$'()<>{}|\\^\[\]`~]/;
/******************************************************************************/
var whitelistChanged = function() {
var s = uDom('#whitelist').val().trim();
var bad = reUnwantedChars.test(s);
uDom('#whitelistApply').prop(
'disabled',
s === cachedWhitelist
s === cachedWhitelist || bad
);
uDom('#whitelist').toggleClass('bad', badWhitelist(s));
uDom('#whitelist').toggleClass('bad', bad);
};
/******************************************************************************/
function renderWhitelist() {
var renderWhitelist = function() {
var onRead = function(whitelist) {
cachedWhitelist = stringFromWhitelist(whitelist);
cachedWhitelist = whitelist;
uDom('#whitelist').val(cachedWhitelist);
};
messaging.ask({ what: 'getWhitelist' }, onRead);
@ -140,7 +103,7 @@ var whitelistApplyHandler = function() {
cachedWhitelist = uDom('#whitelist').val().trim();
var request = {
what: 'setWhitelist',
whitelist: whitelistFromString(cachedWhitelist)
whitelist: cachedWhitelist
};
messaging.tell(request);
whitelistChanged();

View file

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_extName__",
"short_name": "µBlock",
"version": "0.3.1.3",
"version": "0.3.2.0",
"description": "__MSG_extShortDesc__",
"icons": {
"16": "img/icon_16.png",