Skip to content

Commit

Permalink
Reflect blocking mode in badge color of toolbar icon
Browse files Browse the repository at this point in the history
Related feedback:
- https://www.reddit.com/r/uBlockOrigin/comments/cmh910/

Additionally, the `3p` rule has been made distinct from
`3p-script`/`3p-frame` for the purpose of
"Relax blocking mode" command.

The badge color will hint at the current blocking mode.
There are four colors for the four following blocking
modes:
- JavaScript wholly disabled
- All 3rd parties blocked
- 3rd-party scripts and frames blocked
- None of the above

The default badge color will be used when JavaScript is not
wholly disabled and when there are no rules for `3p`,
`3p-script` or `3p-frame`.

A new advanced setting has been added to let the user choose
the badge colors for the various blocking modes,
`blockingProfileColors`. The value *must* be a sequence of
4 valid CSS color values that match 6 hexadecimal digits
prefixed with`#` -- anything else will be ignored.
  • Loading branch information
gorhill committed Aug 10, 2019
1 parent 5e1f4d7 commit 7ff750e
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 63 deletions.
57 changes: 30 additions & 27 deletions platform/chromium/vapi-background.js
Original file line number Diff line number Diff line change
Expand Up @@ -661,28 +661,25 @@ vAPI.Tabs = class {
// https://github.com/uBlockOrigin/uBlock-issues/issues/32
// Ensure ImageData for toolbar icon is valid before use.

vAPI.setIcon = (function() {
const browserAction = chrome.browserAction,
titleTemplate =
chrome.runtime.getManifest().browser_action.default_title +
' ({badge})';
vAPI.setIcon = (( ) => {
const browserAction = chrome.browserAction;
const titleTemplate =

This comment has been minimized.

Copy link
@jspenguin2017

jspenguin2017 May 16, 2020

Contributor

Extra space between const and titleTemplate.

browser.runtime.getManifest().browser_action.default_title +
' ({badge})';
const icons = [
{
path: { '16': 'img/icon_16-off.png', '32': 'img/icon_32-off.png' }
},
{
path: { '16': 'img/icon_16.png', '32': 'img/icon_32.png' }
}
{ path: { '16': 'img/icon_16-off.png', '32': 'img/icon_32-off.png' } },
{ path: { '16': 'img/icon_16.png', '32': 'img/icon_32.png' } },
];

(function() {
(( ) => {
if ( browserAction.setIcon === undefined ) { return; }

// The global badge background color.
// The global badge text and background color.
if ( browserAction.setBadgeBackgroundColor !== undefined ) {
browserAction.setBadgeBackgroundColor({
color: [ 0x66, 0x66, 0x66, 0xFF ]
});
browserAction.setBadgeBackgroundColor({ color: '#666666' });
}
if ( browserAction.setBadgeTextColor !== undefined ) {
browserAction.setBadgeTextColor({ color: '#FFFFFF' });
}

// As of 2018-05, benchmarks show that only Chromium benefits for sure
Expand All @@ -698,7 +695,7 @@ vAPI.setIcon = (function() {

const imgs = [];
for ( let i = 0; i < icons.length; i++ ) {
let path = icons[i].path;
const path = icons[i].path;
for ( const key in path ) {
if ( path.hasOwnProperty(key) === false ) { continue; }
imgs.push({ i: i, p: key });
Expand All @@ -719,10 +716,10 @@ vAPI.setIcon = (function() {
for ( const img of imgs ) {
if ( img.r.complete === false ) { return; }
}
let ctx = document.createElement('canvas').getContext('2d');
let iconData = [ null, null ];
const ctx = document.createElement('canvas').getContext('2d');
const iconData = [ null, null ];
for ( const img of imgs ) {
let w = img.r.naturalWidth, h = img.r.naturalHeight;
const w = img.r.naturalWidth, h = img.r.naturalHeight;
ctx.width = w; ctx.height = h;
ctx.clearRect(0, 0, w, h);
ctx.drawImage(img.r, 0, 0);
Expand Down Expand Up @@ -753,16 +750,23 @@ vAPI.setIcon = (function() {
}
})();

var onTabReady = function(tab, state, badge, parts) {
const onTabReady = function(tab, details) {
if ( vAPI.lastError() || !tab ) { return; }

const { parts, state, badge, color } = details;

if ( browserAction.setIcon !== undefined ) {
if ( parts === undefined || (parts & 0x01) !== 0 ) {
if ( parts === undefined || (parts & 0b001) !== 0 ) {
browserAction.setIcon(
Object.assign({ tabId: tab.id }, icons[state])
);
}
browserAction.setBadgeText({ tabId: tab.id, text: badge });
if ( (parts & 0b010) !== 0 ) {
browserAction.setBadgeText({ tabId: tab.id, text: badge });
}
if ( (parts & 0b100) !== 0 ) {
browserAction.setBadgeBackgroundColor({ tabId: tab.id, color });
}
}

if ( browserAction.setTitle !== undefined ) {
Expand All @@ -778,14 +782,13 @@ vAPI.setIcon = (function() {

// parts: bit 0 = icon
// bit 1 = badge
// bit 2 = badge color

return function(tabId, state, badge, parts) {
return function(tabId, details) {
tabId = toChromiumTabId(tabId);
if ( tabId === 0 ) { return; }

chrome.tabs.get(tabId, function(tab) {
onTabReady(tab, state, badge, parts);
});
browser.tabs.get(tabId, tab => onTabReady(tab, details));

if ( vAPI.contextMenu instanceof Object ) {
vAPI.contextMenu.onMustUpdate(tabId);
Expand Down
3 changes: 2 additions & 1 deletion src/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ const µBlock = (function() { // jshint ignore:line
autoUpdateAssetFetchPeriod: 120,
autoUpdateDelayAfterLaunch: 180,
autoUpdatePeriod: 7,
blockingProfiles: '11101 00001',
blockingProfiles: '11101 11001 00001',
blockingProfileColors: '#666666 #E7552C #F69454 #008DCB',
cacheStorageAPI: 'unset',
cacheStorageCompression: true,
cacheControlForFirefox1376932: 'no-cache, no-store, must-revalidate',
Expand Down
29 changes: 2 additions & 27 deletions src/js/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,40 +39,15 @@
if ( µBlock.canUseShortcuts === false ) { return; }

const relaxBlockingMode = function(tab) {
if (
tab instanceof Object === false ||
tab.id <= 0
) {
return;
}
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 currentProfile = µb.blockingModeFromHostname(hn);
const profiles = [];
for ( const s of µb.hiddenSettings.blockingProfiles.split(/\s+/) ) {
const v = parseInt(s, 2);
Expand Down
3 changes: 2 additions & 1 deletion src/js/messaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ var onMessage = function(request, sender, callback) {
request.srcHostname,
'net'
);
µb.updateToolbarIcon(request.tabId, 0b100);
response = popupDataFromTabId(request.tabId);
break;

Expand Down Expand Up @@ -462,7 +463,7 @@ var onMessage = function(request, sender, callback) {
pageStore = µb.pageStoreFromTabId(request.tabId);
if ( pageStore ) {
pageStore.toggleNetFilteringSwitch(request.url, request.scope, request.state);
µb.updateToolbarIcon(request.tabId, 0x03);
µb.updateToolbarIcon(request.tabId, 0b111);
}
break;

Expand Down
1 change: 1 addition & 0 deletions src/js/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
}
self.log.verbosity = this.hiddenSettings.consoleLogLevel;
resolve();
this.fireDOMEvent('hiddenSettingsChanged');
});

// <<<< end of executor
Expand Down
46 changes: 39 additions & 7 deletions src/js/tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@

// Blocked
if ( µb.userSettings.showIconBadge ) {
µb.updateToolbarIcon(openerTabId, 0x02);
µb.updateToolbarIcon(openerTabId, 0b010);
}

// It is a popup, block and remove the tab.
Expand Down Expand Up @@ -859,7 +859,7 @@ vAPI.tabs = new vAPI.Tabs();
// Create an entry for the tab if it doesn't exist.

µBlock.bindTabToPageStats = function(tabId, context) {
this.updateToolbarIcon(tabId, 0x03);
this.updateToolbarIcon(tabId, 0b111);

// Do not create a page store for URLs which are of no interests
if ( this.tabContextManager.exists(tabId) === false ) {
Expand Down Expand Up @@ -954,6 +954,24 @@ vAPI.tabs = new vAPI.Tabs();

µBlock.updateToolbarIcon = (( ) => {
const tabIdToDetails = new Map();
const blockingProfileColors = [
'#666666',
'#E7552C',
'#F69454',
'#008DCB',
];

self.addEventListener(
'hiddenSettingsChanged',
( ) => {
const colors = µBlock.hiddenSettings.blockingProfileColors;
if ( /^#[0-9a-f]{6}(\s#[0-9a-f]{6}){3}$/i.test(colors) === false ) {
return;
}
blockingProfileColors.length = 0;
blockingProfileColors.push(...colors.split(/\s+/));
}
);

const updateBadge = function(tabId) {
const µb = µBlock;
Expand All @@ -962,26 +980,40 @@ vAPI.tabs = new vAPI.Tabs();

let state = 0;
let badge = '';
let color = blockingProfileColors[0];

let pageStore = µb.pageStoreFromTabId(tabId);
if ( pageStore !== null ) {
state = pageStore.getNetFilteringSwitch() ? 1 : 0;
if (
state === 1 &&
µb.userSettings.showIconBadge &&
pageStore.perLoadBlockedRequestCount
µb.userSettings.showIconBadge
) {
badge = µb.formatCount(pageStore.perLoadBlockedRequestCount);
if ( (parts & 0b010) !== 0 && pageStore.perLoadBlockedRequestCount ) {
badge = µb.formatCount(pageStore.perLoadBlockedRequestCount);
}
if ( (parts & 0b100) !== 0 ) {
let profile = µb.blockingModeFromHostname(pageStore.tabHostname);
if ( (profile & 0b00000010) !== 0 ) {
color = blockingProfileColors[3];
} else if ( (profile & 0b00000100) !== 0 ) {
color = blockingProfileColors[2];
} else if ( (profile & 0b00011000) !== 0 ) {
color = blockingProfileColors[1];
}
}
}
}

vAPI.setIcon(tabId, state, badge, parts);
vAPI.setIcon(tabId, { parts, state, badge, color });
};

// parts: bit 0 = icon
// bit 1 = badge
// bit 2 = badge color

return function(tabId, newParts = 0b11) {
return function(tabId, newParts = 0b111) {
if ( typeof tabId !== 'number' ) { return; }
if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
let currentParts = tabIdToDetails.get(tabId);
if ( currentParts === newParts ) { return; }
Expand Down
30 changes: 30 additions & 0 deletions src/js/ublock.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ const matchBucket = function(url, hostname, bucket, start) {
this.redirectEngine.invalidateResourcesSelfie();
this.loadRedirectResources();
}
this.fireDOMEvent('hiddenSettingsChanged');
};

/******************************************************************************/
Expand Down Expand Up @@ -506,6 +507,10 @@ const matchBucket = function(url, hostname, bucket, start) {

// https://github.com/chrisaljoudi/uBlock/issues/420
this.cosmeticFilteringEngine.removeFromSelectorCache(srcHostname, 'net');

if ( requestType.startsWith('3p') ) {
this.updateToolbarIcon(details.tabId, 0b100);
}
};

/******************************************************************************/
Expand Down Expand Up @@ -548,6 +553,9 @@ const matchBucket = function(url, hostname, bucket, start) {

// Take action if needed
switch ( details.name ) {
case 'no-scripting':
this.updateToolbarIcon(details.tabId, 0b100);
break;
case 'no-cosmetic-filtering':
this.scriptlets.injectDeep(
details.tabId,
Expand Down Expand Up @@ -577,6 +585,28 @@ const matchBucket = function(url, hostname, bucket, start) {

/******************************************************************************/

µBlock.blockingModeFromHostname = function(hn) {
let bits = 0;
if ( this.sessionSwitches.evaluateZ('no-scripting', hn) ) {
bits |= 0b00000010;
}
if ( this.userSettings.advancedUserEnabled ) {
const fw = this.sessionFirewall;
if ( fw.evaluateCellZY(hn, '*', '3p') === 1 ) {
bits |= 0b00000100;
}
if ( fw.evaluateCellZY(hn, '*', '3p-script') === 1 ) {
bits |= 0b00001000;
}
if ( fw.evaluateCellZY(hn, '*', '3p-frame') === 1 ) {
bits |= 0b00010000;
}
}
return bits;
};

/******************************************************************************/

// https://github.com/NanoMeow/QuickReports/issues/6#issuecomment-414516623
// Inject as early as possible to make the cosmetic logger code less
// sensitive to the removal of DOM nodes which may match injected
Expand Down
12 changes: 12 additions & 0 deletions src/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -700,3 +700,15 @@
return datasetPromise;
};
})();

/******************************************************************************/

µBlock.fireDOMEvent = function(name) {
if (
window instanceof Object &&
window.dispatchEvent instanceof Function &&
window.CustomEvent instanceof Function
) {
window.dispatchEvent(new CustomEvent(name));
}
};

5 comments on commit 7ff750e

@gwarser
Copy link
Contributor

@gwarser gwarser commented on 7ff750e Aug 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I'm now experimenting with different sequence of blockingProfiles: 11011 11001 00001 (hard mode + no-scripting - switch off 3rd-party first) and it's sometimes bit confusing when badge color do not change after pressing shortcut.

@gorhill
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There has to be a priority with the blocking colors, two colors can't be displayed at the same time. The JavaScript switch has priority, then 3p, then 3p-script/frame. Are you arguing that 3p should have priority over the JavaScript switch?

@gorhill
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to see if I can re-work how the rule bits and colors are encoded in hidden settings.

@gorhill
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does make sense to allow passive 3rd-party resources before JavaScript, isn't? A site has more chance to render properly if allowed to fetch 3rd-party CSS for example before enabling JavaScript. So if I am going to differentiate between 3p and 3p-script/frame, I should make it so 3p is disabled before JavaScript.

@gwarser
Copy link
Contributor

@gwarser gwarser commented on 7ff750e Aug 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does make sense to allow passive 3rd-party resources before JavaScript, isn't? A site has more chance to render properly if allowed to fetch 3rd-party CSS for example before enabling JavaScript.

Yes, for example: https://blog.qt.io/ and https://www.fakt.pl/(images are visible in particular articles). Maybe not completely fixed, but easier to read.

Please sign in to comment.