From 693687fd74fe9a4645f0c9c1e6dbedb56b5fb5d7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 26 Jun 2019 07:47:14 -0400 Subject: [PATCH] Add keyboard support for toggling down blocking profile Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/371 By default, no specific keyboard shortcut is predefined, this will have to be assigned by the user. The command name in English is "Toggle blocking profile". The default behavior is to toggle down according to one of the following scenarios. a) If script execution is disabled through the no-scripting switch, the no-scripting switch will be locally toggled so as to allow script execution. The page will be automatically reloaded. b) If script execution is not blocked but the 3rd-party script and/or frame cells are blocked, local no-op rules will be set so as to no longer block 3rd-party scripts and/or frames. The page will be automatically reloaded. Given this, it may take more than one toggle down command to reach the lowest blocking profile, which is one where JavaScript execution is not blocked and 3rd-party scripts and frames resources block rules, if any, are bypassed with local no-op rules. TODO: At this point, I haven't yet decided whether toggling from the lowest profile should restore the original highest blocking profile. --- platform/chromium/manifest.json | 3 + platform/chromium/vapi-background.js | 2 +- src/_locales/en/messages.json | 4 + src/js/background.js | 1 + src/js/commands.js | 173 ++++++++++++++++++++++----- src/js/ublock.js | 47 ++++++-- 6 files changed, 190 insertions(+), 40 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 1edcab985..e5d0b98d1 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -20,6 +20,9 @@ }, "launch-logger": { "description": "__MSG_popupTipLog__" + }, + "toggle-blocking-profile": { + "description": "__MSG_toggleBlockingProfile__" } }, "content_scripts": [ diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 301577a48..66c2249bf 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -607,7 +607,7 @@ vAPI.tabs.remove = function(tabId) { /******************************************************************************/ -vAPI.tabs.reload = function(tabId, bypassCache) { +vAPI.tabs.reload = function(tabId, bypassCache = false) { tabId = toChromiumTabId(tabId); if ( tabId === 0 ) { return; } diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index b65f6320a..6afcd9702 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -963,6 +963,10 @@ "message":"Copy to clipboard", "description":"Label for buttons used to copy something to the clipboard" }, + "toggleBlockingProfile":{ + "message":"Toggle blocking profile", + "description":"Label for keyboard shortcut used to toggle blocking profile" + }, "dummy":{ "message":"This entry must be the last one", "description":"so we dont need to deal with comma for last entry" diff --git a/src/js/background.js b/src/js/background.js index 036c9b309..ff756ea43 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -42,6 +42,7 @@ const µBlock = (function() { // jshint ignore:line autoUpdateAssetFetchPeriod: 120, autoUpdateDelayAfterLaunch: 180, autoUpdatePeriod: 7, + blockingProfiles: '11111 11101 00001', cacheStorageAPI: 'unset', cacheStorageCompression: true, cacheControlForFirefox1376932: 'no-cache, no-store, must-revalidate', diff --git a/src/js/commands.js b/src/js/commands.js index a8036cd4d..3b5abd05b 100644 --- a/src/js/commands.js +++ b/src/js/commands.js @@ -26,43 +26,160 @@ /******************************************************************************/ µBlock.canUseShortcuts = vAPI.commands instanceof Object; - µBlock.canUpdateShortcuts = µBlock.canUseShortcuts && typeof vAPI.commands.update === 'function'; /******************************************************************************/ -(function() { - if ( µBlock.canUseShortcuts === false ) { return; } +(( ) => { - vAPI.commands.onCommand.addListener(function(command) { - var µb = µBlock; +// ***************************************************************************** +// start of local namespace - switch ( command ) { - case 'launch-element-zapper': - case 'launch-element-picker': - vAPI.tabs.get(null, function(tab) { - if ( tab instanceof Object === false ) { return; } - µb.mouseEventRegister.x = µb.mouseEventRegister.y = -1; - µb.elementPickerExec(tab.id, undefined, command === 'launch-element-zapper'); - }); - break; - case 'launch-logger': - vAPI.tabs.get(null, function(tab) { - let hash = tab.url.startsWith(vAPI.getURL('')) ? - '' : - '#_+' + tab.id; - µb.openNewTab({ - url: 'logger-ui.html' + hash, - select: true, - index: -1 - }); - }); - break; - default: +if ( µBlock.canUseShortcuts === false ) { return; } + +const toggleBlockingProfile = function(tab) { + if ( + tab instanceof Object === false || + tab.id <= 0 + ) { + return; + } + + const µb = µBlock; + const normalURL = µb.normalizePageURL(tab.id, tab.url); + + if ( µb.getNetFilteringSwitch(normalURL) === false ) { return; } + + const hn = µb.URI.hostnameFromURI(normalURL); + + // Construct current blocking profile + const ssw = µb.sessionSwitches; + const sfw = µb.sessionFirewall; + let currentProfile = 0; + + if ( ssw.evaluateZ('no-scripting', hn) ) { + currentProfile |= 0b00000010; + } + if ( µb.userSettings.advancedUserEnabled ) { + if ( sfw.evaluateCellZY(hn, '*', '3p') === 1 ) { + currentProfile |= 0b00000100; + } + if ( sfw.evaluateCellZY(hn, '*', '3p-script') === 1 ) { + currentProfile |= 0b00001000; + } + if ( sfw.evaluateCellZY(hn, '*', '3p-frame') === 1 ) { + currentProfile |= 0b00010000; + } + } + + const profiles = []; + for ( const s of µb.hiddenSettings.blockingProfiles.split(/\s+/) ) { + const v = parseInt(s, 2); + if ( isNaN(v) ) { continue; } + profiles.push(v); + } + let newProfile; + for ( const profile of profiles ) { + if ( (currentProfile & profile & 0b11111110) !== currentProfile ) { + newProfile = profile; break; } - }); + } + + // TODO: Reset to original blocking profile? + if ( newProfile === undefined ) { return; } + + if ( + (currentProfile & 0b00000010) !== 0 && + (newProfile & 0b00000010) === 0 + ) { + µb.toggleHostnameSwitch({ + name: 'no-scripting', + hostname: hn, + state: false, + }); + } + if ( µb.userSettings.advancedUserEnabled ) { + if ( + (currentProfile & 0b00000100) !== 0 && + (newProfile & 0b00000100) === 0 + ) { + µb.toggleFirewallRule({ + srcHostname: hn, + desHostname: '*', + requestType: '3p', + action: 3, + }); + } + if ( + (currentProfile & 0b00001000) !== 0 && + (newProfile & 0b00001000) === 0 + ) { + µb.toggleFirewallRule({ + srcHostname: hn, + desHostname: '*', + requestType: '3p-script', + action: 3, + }); + } + if ( + (currentProfile & 0b00010000) !== 0 && + (newProfile & 0b00010000) === 0 + ) { + µb.toggleFirewallRule({ + srcHostname: hn, + desHostname: '*', + requestType: '3p-frame', + action: 3, + }); + } + } + + if ( newProfile & 0b00000001 ) { + vAPI.tabs.reload(tab.id); + } +}; + +vAPI.commands.onCommand.addListener(command => { + const µb = µBlock; + + switch ( command ) { + case 'launch-element-picker': + case 'launch-element-zapper': + vAPI.tabs.get(null, tab => { + if ( tab instanceof Object === false ) { return; } + µb.mouseEventRegister.x = µb.mouseEventRegister.y = -1; + µb.elementPickerExec( + tab.id, + undefined, + command === 'launch-element-zapper' + ); + }); + break; + case 'launch-logger': + vAPI.tabs.get(null, tab => { + const hash = tab.url.startsWith(vAPI.getURL('')) + ? '' + : `#_+${tab.id}`; + µb.openNewTab({ + url: `logger-ui.html${hash}`, + select: true, + index: -1 + }); + }); + break; + case 'toggle-blocking-profile': + vAPI.tabs.get(null, toggleBlockingProfile); + break; + default: + break; + } +}); + +// end of local namespace +// ***************************************************************************** + })(); /******************************************************************************/ diff --git a/src/js/ublock.js b/src/js/ublock.js index 68130cd8b..d151f12ef 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -24,11 +24,11 @@ /******************************************************************************/ /******************************************************************************/ +{ + // ***************************************************************************** // start of local namespace -{ - // https://github.com/chrisaljoudi/uBlock/issues/405 // Be more flexible with whitelist syntax @@ -451,20 +451,39 @@ const matchBucket = function(url, hostname, bucket, start) { // (but not really) redundant rules led to this issue. µBlock.toggleFirewallRule = function(details) { - let requestType = details.requestType; + let { srcHostname, desHostname, requestType, action } = details; - if ( details.action !== 0 ) { - this.sessionFirewall.setCell(details.srcHostname, details.desHostname, requestType, details.action); + if ( action !== 0 ) { + this.sessionFirewall.setCell( + srcHostname, + desHostname, + requestType, + action + ); } else { - this.sessionFirewall.unsetCell(details.srcHostname, details.desHostname, requestType); + this.sessionFirewall.unsetCell( + srcHostname, + desHostname, + requestType + ); } // https://github.com/chrisaljoudi/uBlock/issues/731#issuecomment-73937469 if ( details.persist ) { - if ( details.action !== 0 ) { - this.permanentFirewall.setCell(details.srcHostname, details.desHostname, requestType, details.action); + if ( action !== 0 ) { + this.permanentFirewall.setCell( + srcHostname, + desHostname, + requestType, + action + ); } else { - this.permanentFirewall.unsetCell(details.srcHostname, details.desHostname, requestType, details.action); + this.permanentFirewall.unsetCell( + srcHostname, + desHostname, + requestType, + action + ); } this.savePermanentFirewallRules(); } @@ -473,10 +492,14 @@ const matchBucket = function(url, hostname, bucket, start) { // Flush all cached `net` cosmetic filters if we are dealing with a // collapsible type: any of the cached entries could be a resource on the // target page. - let srcHostname = details.srcHostname; if ( (srcHostname !== '*') && - (requestType === '*' || requestType === 'image' || requestType === '3p' || requestType === '3p-frame') + ( + requestType === '*' || + requestType === 'image' || + requestType === '3p' || + requestType === '3p-frame' + ) ) { srcHostname = '*'; } @@ -635,3 +658,5 @@ const matchBucket = function(url, hostname, bucket, start) { report: report }; })(); + +/******************************************************************************/