1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-20 01:12:38 +01:00

new switch: toggle cosmetic filtering on/off for a site

This commit is contained in:
gorhill 2015-04-05 12:03:14 -04:00
parent 6eb6d2b01d
commit 2dde6f15de
20 changed files with 461 additions and 41 deletions

View File

@ -65,7 +65,7 @@ vAPI.tabs = {};
/******************************************************************************/
vAPI.isNoTabId = function(tabId) {
vAPI.isBehindTheSceneTabId = function(tabId) {
return tabId.toString() === '-1';
};

View File

@ -333,7 +333,7 @@ var tabWatcher = {
/******************************************************************************/
vAPI.isNoTabId = function(tabId) {
vAPI.isBehindTheSceneTabId = function(tabId) {
return tabId.toString() === '-1';
};

View File

@ -239,7 +239,7 @@
/******************************************************************************/
vAPI.isNoTabId = function(tabId) {
vAPI.isBehindTheSceneTabId = function(tabId) {
return tabId.toString() === this.noTabId;
};

View File

@ -76,12 +76,16 @@
"description":"English: Go to request log"
},
"popupTipNoPopups":{
"message":"No popups for this site",
"description":"English: No popups for this site"
"message":"Toggle the blocking of all popups for this site",
"description":"English: Toggle the blocking of all popups for this site"
},
"popupTipNoStrictBlocking":{
"message":"No strict blocking for this site",
"description":"English: No strict blocking for this site"
"message":"Toggle strict blocking for this site",
"description":"English: Toggle strict blocking for this site"
},
"popupTipNoCosmeticFiltering":{
"message":"Toggle cosmetic filtering for this site",
"description":"English: Toggle cosmetic filtering for this site"
},
"popupAnyRulePrompt":{
"message":"all",

View File

@ -148,7 +148,14 @@ body.off #switch .fa {
margin: 0 0.5em;
position: relative;
}
#extraTools > span.on > span {
#extraTools > span > span.badge {
color: #000;
bottom: -2px;
font: 10px sans-serif;
left: 100%;
position: absolute;
}
#extraTools > span.on > span:last-of-type {
color: #e00;
font-size: 20px;
left: 0;
@ -157,7 +164,7 @@ body.off #switch .fa {
top: 0;
width: 100%;
}
#extraTools > span.on > span:after {
#extraTools > span.on > span:last-of-type:after {
content: '\2715';
}
@ -176,7 +183,7 @@ body.advancedUser #panes.dfEnabled h2:before {
background-color: #ffe;
border: 1px solid #eec;
border-radius: 4px;
bottom: 4px;
bottom: 0.7em;
color: #888;
cursor: pointer;
display: none;

View File

@ -187,7 +187,7 @@ return asyncJobManager;
};
var updateBadgeAsync = function(tabId) {
if ( vAPI.isNoTabId(tabId) ) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return;
}
µb.asyncJobs.add('updateBadge-' + tabId, tabId, updateBadge, 250);

View File

@ -55,6 +55,7 @@ if ( vAPI.contentscriptEndInjected ) {
return;
}
vAPI.contentscriptEndInjected = true;
vAPI.styles = vAPI.styles || [];
/******************************************************************************/
@ -326,12 +327,12 @@ var uBlockCollapser = (function() {
nextRetrieveHandler(response);
};
var nextRetrieveHandler = function(selectors) {
var nextRetrieveHandler = function(response) {
//var tStart = timer.now();
//console.debug('µBlock> contextNodes = %o', contextNodes);
var hideSelectors = [];
if ( selectors && selectors.hide.length ) {
processLowGenerics(selectors.hide, hideSelectors);
if ( response && response.hide.length ) {
processLowGenerics(response.hide, hideSelectors);
}
if ( highGenerics ) {
if ( highGenerics.hideLowCount ) {
@ -360,14 +361,15 @@ var uBlockCollapser = (function() {
var addStyleTag = function(selectors) {
var selectorStr = selectors.toString();
hideElements(selectorStr);
var style = document.createElement('style');
// The linefeed before the style block is very important: do no remove!
style.appendChild(document.createTextNode(selectorStr + '\n{display:none !important;}'));
var parent = document.body || document.documentElement;
if ( parent ) {
parent.appendChild(style);
vAPI.styles.push(style);
}
hideElements(selectorStr);
messager.send({
what: 'injectedSelectors',
type: 'cosmetic',

View File

@ -53,6 +53,7 @@ if ( vAPI.contentscriptStartInjected ) {
return;
}
vAPI.contentscriptStartInjected = true;
vAPI.styles = vAPI.styles || [];
/******************************************************************************/
@ -91,7 +92,6 @@ var cosmeticFilters = function(details) {
}
if ( hide.length !== 0 ) {
var text = hide.join(',\n');
hideElements(text);
var style = vAPI.specificHideStyle = document.createElement('style');
// The linefeed before the style block is very important: do not remove!
style.appendChild(document.createTextNode(text + '\n{display:none !important;}'));
@ -99,7 +99,9 @@ var cosmeticFilters = function(details) {
var parent = document.head || document.documentElement;
if ( parent ) {
parent.appendChild(style);
vAPI.styles.push(style);
}
hideElements(text);
}
vAPI.donthideCosmeticFilters = donthideCosmeticFilters;
vAPI.hideCosmeticFilters = hideCosmeticFilters;

82
src/js/cosmetic-count.js Normal file
View File

@ -0,0 +1,82 @@
/*******************************************************************************
uBlock - a browser extension to block requests.
Copyright (C) 2015 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
/* global vAPI, HTMLDocument */
/******************************************************************************/
(function() {
'use strict';
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/464
if ( document instanceof HTMLDocument === false ) {
//console.debug('cosmetic-on.js > not a HTLMDocument');
return;
}
// Because in case
if ( !vAPI ) {
//console.debug('cosmetic-on.js > vAPI not found');
return;
}
/******************************************************************************/
// Insert all cosmetic filtering-related style tags in the DOM
var selectors = [];
var reProperties = /\s*\{[^}]+\}\s*/;
var i;
var styles = vAPI.styles || [];
i = styles.length;
while ( i-- ) {
selectors.push(styles[i].textContent.replace(reProperties, ''));
}
var elems = [];
if ( selectors.length !== 0 ) {
try {
elems = document.querySelectorAll(selectors.join(','));
} catch (e) {
}
}
/******************************************************************************/
var localMessager = vAPI.messaging.channel('cosmetic-*.js');
localMessager.send({
what: 'hiddenElementCount',
count: elems.length
}, function() {
localMessager.close();
});
/******************************************************************************/
})();
/******************************************************************************/

94
src/js/cosmetic-off.js Normal file
View File

@ -0,0 +1,94 @@
/*******************************************************************************
uBlock - a browser extension to block requests.
Copyright (C) 2015 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
/* global vAPI, HTMLDocument */
/******************************************************************************/
(function() {
'use strict';
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/464
if ( document instanceof HTMLDocument === false ) {
//console.debug('cosmetic-off.js > not a HTLMDocument');
return;
}
// Because in case
if ( !vAPI ) {
//console.debug('cosmetic-off.js > vAPI not found');
return;
}
/******************************************************************************/
var styles = vAPI.styles;
if ( Array.isArray(styles) === false ) {
return;
}
/******************************************************************************/
// Remove all cosmetic filtering-related styles from the DOM
var selectors = [];
var reProperties = /\s*\{[^}]+\}\s*/;
var style, i;
i = styles.length;
while ( i-- ) {
style = styles[i];
if ( style.parentElement === null ) {
continue;
}
style.parentElement.removeChild(style);
selectors.push(style.textContent.replace(reProperties, ''));
}
// Remove `display: none !important` attribute
if ( selectors.length === 0 ) {
return;
}
var elems = [];
try {
elems = document.querySelectorAll(selectors.join(','));
} catch (e) {
}
i = elems.length;
while ( i-- ) {
style = elems[i].style;
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
style.removeProperty('display');
}
}
/******************************************************************************/
})();
/******************************************************************************/

98
src/js/cosmetic-on.js Normal file
View File

@ -0,0 +1,98 @@
/*******************************************************************************
uBlock - a browser extension to block requests.
Copyright (C) 2015 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
/* global vAPI, HTMLDocument */
/******************************************************************************/
(function() {
'use strict';
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/464
if ( document instanceof HTMLDocument === false ) {
//console.debug('cosmetic-on.js > not a HTLMDocument');
return;
}
// Because in case
if ( !vAPI ) {
//console.debug('cosmetic-on.js > vAPI not found');
return;
}
/******************************************************************************/
var styles = vAPI.styles;
if ( Array.isArray(styles) === false ) {
return;
}
/******************************************************************************/
// Insert all cosmetic filtering-related style tags in the DOM
var selectors = [];
var reProperties = /\s*\{[^}]+\}\s*/;
var style, i;
var parent = document.head || document.body || document.documentElement;
i = styles.length;
while ( i-- ) {
style = styles[i];
if ( style.parentElement !== null ) {
continue;
}
if ( parent === null ) {
continue;
}
selectors.push(style.textContent.replace(reProperties, ''));
parent.appendChild(style);
}
// Add `display: none !important` attribute
if ( selectors.length === 0 ) {
return;
}
var elems = [];
try {
elems = document.querySelectorAll(selectors.join(','));
} catch (e) {
}
i = elems.length;
while ( i-- ) {
style = elems[i].style;
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
style.setProperty('display', 'none', 'important');
}
}
/******************************************************************************/
})();
/******************************************************************************/

View File

@ -60,7 +60,7 @@ var proceedTemporary = function() {
var proceedPermanent = function() {
messager.send({
what: 'toggleHostnameSwitch',
name: 'dontBlockDoc',
name: 'noStrictBlocking',
hostname: details.hn,
state: true
}, proceedToURL);

View File

@ -37,8 +37,14 @@ var HnSwitches = function() {
/******************************************************************************/
var switchBitOffsets = {
'dontBlockDoc': 0,
'doBlockAllPopups': 2
'noStrictBlocking': 0,
'noPopups': 2,
'noCosmeticFiltering': 4
};
var fromLegacySwitchNames = {
'dontBlockDoc': 'noStrictBlocking',
'doBlockAllPopups': 'noPopups'
};
var switchStateToNameMap = {
@ -253,6 +259,7 @@ HnSwitches.prototype.fromString = function(text) {
continue;
}
switchName = switchName.slice(0, pos);
switchName = fromLegacySwitchNames[switchName] || switchName;
if ( switchBitOffsets.hasOwnProperty(switchName) === false ) {
continue;
}

View File

@ -76,7 +76,7 @@ var onMessage = function(request, sender, callback) {
break;
case 'reloadTab':
if ( vAPI.isNoTabId(request.tabId) === false ) {
if ( vAPI.isBehindTheSceneTabId(request.tabId) === false ) {
vAPI.tabs.reload(request.tabId);
if ( request.select && vAPI.tabs.select ) {
vAPI.tabs.select(request.tabId);
@ -233,8 +233,9 @@ var getStats = function(tabId, tabTitle) {
r.firewallRules = getFirewallRules(pageStore.pageHostname, r.hostnameDict);
r.canElementPicker = r.pageHostname.indexOf('.') !== -1;
r.canRequestLog = canRequestLog;
r.doBlockAllPopups = µb.hnSwitches.evaluateZ('doBlockAllPopups', r.pageHostname);
r.dontBlockDoc = µb.hnSwitches.evaluateZ('dontBlockDoc', r.pageHostname);
r.noPopups = µb.hnSwitches.evaluateZ('noPopups', r.pageHostname);
r.noStrictBlocking = µb.hnSwitches.evaluateZ('noStrictBlocking', r.pageHostname);
r.noCosmeticFiltering = µb.hnSwitches.evaluateZ('noCosmeticFiltering', r.pageHostname);
} else {
r.hostnameDict = {};
r.firewallRules = getFirewallRules();
@ -284,9 +285,32 @@ var getTargetTabId = function(tab) {
/******************************************************************************/
var getPopupDataLazy = function(tabId, callback) {
var r = {
hiddenElementCount: ''
};
var pageStore = µb.pageStoreFromTabId(tabId);
if ( !pageStore ) {
callback(r);
return;
}
µb.getHiddenElementCount(tabId, function() {
r.hiddenElementCount = pageStore.hiddenElementCount;
callback(r);
});
};
/******************************************************************************/
var onMessage = function(request, sender, callback) {
// Async
switch ( request.what ) {
case 'getPopupDataLazy':
getPopupDataLazy(request.tabId, callback);
return;
case 'getPopupData':
if ( request.tabId === vAPI.noTabId ) {
callback(getStats(vAPI.noTabId, ''));
@ -522,6 +546,56 @@ vAPI.messaging.listen('contentscript-end.js', onMessage);
/******************************************************************************/
/******************************************************************************/
// cosmetic-*.js
(function() {
'use strict';
/******************************************************************************/
var µb = µBlock;
/******************************************************************************/
var onMessage = function(request, sender, callback) {
// Async
switch ( request.what ) {
default:
break;
}
// Sync
var response;
var pageStore;
if ( sender && sender.tab ) {
pageStore = µb.pageStoreFromTabId(sender.tab.id);
}
switch ( request.what ) {
case 'hiddenElementCount':
if ( pageStore ) {
pageStore.hiddenElementCount = request.count;
}
break;
default:
return vAPI.messaging.UNHANDLED;
}
callback(response);
};
vAPI.messaging.listen('cosmetic-*.js', onMessage);
/******************************************************************************/
})();
/******************************************************************************/
/******************************************************************************/
// element-picker.js
(function() {

View File

@ -488,6 +488,7 @@ PageStore.prototype.init = function(tabId, rawURL, pageURL) {
this.netFilteringReadTime = 0;
this.perLoadBlockedRequestCount = 0;
this.perLoadAllowedRequestCount = 0;
this.hiddenElementCount = ''; // Empty string means "unknown"
this.skipLocalMirroring = false;
this.netFilteringCache = NetFilteringResultCache.factory();
@ -614,6 +615,7 @@ PageStore.prototype.getNetFilteringSwitch = function() {
PageStore.prototype.getSpecificCosmeticFilteringSwitch = function() {
return this.getNetFilteringSwitch() &&
µb.hnSwitches.evaluateZ('noCosmeticFiltering', this.rootHostname) !== true &&
(µb.userSettings.advancedUserEnabled &&
µb.sessionFirewall.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false;
};
@ -623,6 +625,7 @@ PageStore.prototype.getSpecificCosmeticFilteringSwitch = function() {
PageStore.prototype.getGenericCosmeticFilteringSwitch = function() {
return this.getNetFilteringSwitch() &&
this.skipCosmeticFiltering === false &&
µb.hnSwitches.evaluateZ('noCosmeticFiltering', this.rootHostname) !== true &&
(µb.userSettings.advancedUserEnabled &&
µb.sessionFirewall.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false;
};

View File

@ -143,9 +143,11 @@ var hashFromPopupData = function(reset) {
hasher.push(rule);
}
}
hasher.sort();
hasher.push(uDom('body').hasClass('off'));
hasher.push(uDom('#noCosmeticFiltering').hasClass('on'));
var hash = hasher.sort().join('');
var hash = hasher.join('');
if ( reset ) {
cachedPopupHash = hash;
}
@ -430,8 +432,9 @@ var renderPopup = function() {
renderPrivacyExposure();
// Extra tools
uDom('#doBlockAllPopups').toggleClass('on', popupData.doBlockAllPopups === true);
uDom('#dontBlockDoc').toggleClass('on', popupData.dontBlockDoc === true);
uDom('#noPopups').toggleClass('on', popupData.noPopups === true);
uDom('#noStrictBlocking').toggleClass('on', popupData.noStrictBlocking === true);
uDom('#noCosmeticFiltering').toggleClass('on', popupData.noCosmeticFiltering === true);
// https://github.com/gorhill/uBlock/issues/470
// This must be done here, to be sure the popup is resized properly
@ -457,6 +460,19 @@ var renderPopup = function() {
/******************************************************************************/
var renderPopupLazy = function() {
var onDataReady = function(data) {
uDom('#noCosmeticFiltering > span.badge').text(data.hiddenElementCount);
};
messager.send({
what: 'getPopupDataLazy',
tabId: popupData.tabId
}, onDataReady);
};
/******************************************************************************/
var toggleNetFilteringSwitch = function(ev) {
if ( !popupData || !popupData.pageURL ) {
return;
@ -666,8 +682,10 @@ var toggleHostnameSwitch = function() {
what: 'toggleHostnameSwitch',
name: switchName,
hostname: popupData.pageHostname,
state: elem.hasClass('on')
state: elem.hasClass('on'),
tabId: popupData.tabId
});
hashFromPopupData();
};
/******************************************************************************/
@ -729,6 +747,7 @@ var getPopupData = function(tabId) {
var onDataReceived = function(response) {
cachePopupData(response);
renderPopup();
renderPopupLazy(); // low priority rendering
hashFromPopupData(true);
pollForContentChange();
};

View File

@ -127,8 +127,8 @@ vAPI.tabs.onPopup = function(details) {
var result = '';
// Check user switch first
if ( µb.hnSwitches.evaluateZ('doBlockAllPopups', openerHostname) ) {
result = 'ub:doBlockAllPopups true';
if ( µb.hnSwitches.evaluateZ('noPopups', openerHostname) ) {
result = 'ub:noPopups true';
}
// https://github.com/gorhill/uBlock/issues/323
@ -177,7 +177,7 @@ vAPI.tabs.registerListeners();
// rules which will apply only to that scheme.
µb.normalizePageURL = function(tabId, pageURL) {
if ( vAPI.isNoTabId(tabId) ) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return 'http://behind-the-scene/';
}
var uri = this.URI.set(pageURL);
@ -200,7 +200,9 @@ vAPI.tabs.registerListeners();
// Create an entry for the tab if it doesn't exist.
µb.bindTabToPageStats = function(tabId, pageURL, context) {
if ( vAPI.isBehindTheSceneTabId(tabId) === false ) {
this.updateBadgeAsync(tabId);
}
// https://github.com/gorhill/httpswitchboard/issues/303
// Normalize page URL
@ -220,6 +222,12 @@ vAPI.tabs.registerListeners();
return this.pageStores[tabId] = this.PageStore.factory(tabId, pageURL, normalURL);
}
// https://github.com/chrisaljoudi/uBlock/issues/1176
// Never rebind behind-the-scene scope
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return pageStore;
}
// https://github.com/gorhill/uBlock/issues/516
// If context if 'beforeRequest', do not rebind
if ( context === 'beforeRequest' ) {
@ -300,7 +308,7 @@ var pageStoreJanitor = function() {
for ( var i = pageStoreJanitorSampleAt; i < n; i++ ) {
tabId = tabIds[i];
// Do not remove behind-the-scene page store
if ( vAPI.isNoTabId(tabId) ) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
continue;
}
checkTab(tabId);

View File

@ -62,7 +62,7 @@ var onBeforeRequest = function(details) {
// Special treatment: behind-the-scene requests
var tabId = details.tabId;
if ( vAPI.isNoTabId(tabId) ) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return onBeforeBehindTheSceneRequest(details);
}
@ -201,8 +201,8 @@ var onBeforeRootFrameRequest = function(details) {
var result = '';
// Permanently unrestricted?
if ( result === '' && µb.hnSwitches.evaluateZ('dontBlockDoc', requestHostname) ) {
result = 'ua:dontBlockDoc true';
if ( result === '' && µb.hnSwitches.evaluateZ('noStrictBlocking', requestHostname) ) {
result = 'ua:noStrictBlocking true';
}
// Temporarily whitelisted?
@ -351,7 +351,7 @@ var onBeforeBehindTheSceneRequest = function(details) {
var onHeadersReceived = function(details) {
// Do not interfere with behind-the-scene requests.
var tabId = details.tabId;
if ( vAPI.isNoTabId(tabId) ) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return;
}

View File

@ -322,6 +322,25 @@ var matchWhitelistDirective = function(url, hostname, directive) {
if ( this.hnSwitches.toggleZ(details.name, details.hostname, details.state) ) {
this.saveHostnameSwitches();
}
// Take action if needed
if ( details.name === 'noCosmeticFiltering' ) {
vAPI.tabs.injectScript(details.tabId, {
file: 'js/cosmetic-' + (details.state ? 'off' : 'on') + '.js',
allFrames: true
});
return;
}
// Whatever else
// ...
};
/******************************************************************************/
µBlock.getHiddenElementCount = function(tabId, callback) {
callback = callback || this.noopFunc;
vAPI.tabs.injectScript(tabId, { file: 'js/cosmetic-count.js' }, callback);
};
/******************************************************************************/

View File

@ -27,8 +27,9 @@
<h2 data-i18n="popupHitDomainCountPrompt">&nbsp;</h2>
<p class="statValue" id="popupHitDomainCount">&nbsp;</p>
<div id="extraTools">
<span id="doBlockAllPopups" class="hnSwitch fa" data-i18n-tip="popupTipNoPopups" data-tip-anchor="topcenter">&#xf0c5;<span></span></span>
<span id="dontBlockDoc" class="hnSwitch fa" data-i18n-tip="popupTipNoStrictBlocking" data-tip-anchor="topcenter">&#xf071;<span></span></span>
<span id="noPopups" class="hnSwitch fa" data-i18n-tip="popupTipNoPopups" data-tip-anchor="topcenter">&#xf0c5;<span></span></span>
<span id="noStrictBlocking" class="hnSwitch fa" data-i18n-tip="popupTipNoStrictBlocking" data-tip-anchor="topcenter">&#xf071;<span></span></span>
<span id="noCosmeticFiltering" class="hnSwitch fa" data-i18n-tip="popupTipNoCosmeticFiltering" data-tip-anchor="topcenter">&#xf070;<span class="badge"></span><span></span></span>
</div>
<div id="refresh" class="fa">&#xf021;</div>
</div><!-- DO NOT REMOVE --><div>