Skip to content

Commit

Permalink
Add userSettings entry to managed storage
Browse files Browse the repository at this point in the history
The managed `userSettings` entry is an array of entries,
where each entry is a name/value pair encoded into an array
of strings.

The first item in the entry array is the name of a setting,
and the second item is the stringified value for the
setting.

This is a more convenient way for administrators to set
specific user settings. The settings set through
`userSettings` policy will always be set at uBO launch
time.
  • Loading branch information
gorhill committed Jan 16, 2021
1 parent 2f4952e commit 6eb1246
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 60 deletions.
9 changes: 9 additions & 0 deletions platform/chromium/managed_storage.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@
"items": { "type": "string" }
}
},
"userSettings": {
"title": "A list of [name,value] pairs to populate user settings",
"type": "array",
"items": {
"title": "A [name,value] pair",
"type": "array",
"items": { "type": "string" }
}
},
"disableDashboard": {
"title": "Set to true to prevent access to configuration options",
"type": "boolean"
Expand Down
47 changes: 25 additions & 22 deletions src/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,29 +83,32 @@ const µBlock = (( ) => { // jshint ignore:line
userResourcesLocation: 'unset',
};

const userSettingsDefault = {
advancedUserEnabled: false,
alwaysDetachLogger: true,
autoUpdate: true,
cloudStorageEnabled: false,
collapseBlocked: true,
colorBlindFriendly: false,
contextMenuEnabled: true,
dynamicFilteringEnabled: false,
externalLists: [],
firewallPaneMinimized: true,
hyperlinkAuditingDisabled: true,
ignoreGenericCosmeticFilters: vAPI.webextFlavor.soup.has('mobile'),
largeMediaSize: 50,
parseAllABPHideFilters: true,
popupPanelSections: 0b111,
prefetchingDisabled: true,
requestLogMaxEntries: 1000,
showIconBadge: true,
tooltipsDisabled: false,
webrtcIPAddressHidden: false,
};

return {
userSettings: {
advancedUserEnabled: false,
alwaysDetachLogger: true,
autoUpdate: true,
cloudStorageEnabled: false,
collapseBlocked: true,
colorBlindFriendly: false,
contextMenuEnabled: true,
dynamicFilteringEnabled: false,
externalLists: [],
firewallPaneMinimized: true,
hyperlinkAuditingDisabled: true,
ignoreGenericCosmeticFilters: vAPI.webextFlavor.soup.has('mobile'),
largeMediaSize: 50,
parseAllABPHideFilters: true,
popupPanelSections: 0b111,
prefetchingDisabled: true,
requestLogMaxEntries: 1000,
showIconBadge: true,
tooltipsDisabled: false,
webrtcIPAddressHidden: false,
},
userSettingsDefault: userSettingsDefault,
userSettings: Object.assign({}, userSettingsDefault),

hiddenSettingsDefault: hiddenSettingsDefault,
hiddenSettingsAdmin: {},
Expand Down
8 changes: 4 additions & 4 deletions src/js/messaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -912,12 +912,12 @@ const backupUserData = async function() {
const userData = {
timeStamp: Date.now(),
version: vAPI.app.version,
userSettings: µb.userSettings,
userSettings:
µb.getModifiedSettings(µb.userSettings, µb.userSettingsDefault),
selectedFilterLists: µb.selectedFilterLists,
hiddenSettings: µb.getModifiedHiddenSettings(),
hiddenSettings:
µb.getModifiedSettings(µb.hiddenSettings, µb.hiddenSettingsDefault),
whitelist: µb.arrayFromWhitelist(µb.netWhitelist),
// String representation eventually to be deprecated
netWhitelist: µb.stringFromWhitelist(µb.netWhitelist),
dynamicFilteringString: µb.permanentFirewall.toString(),
urlFilteringString: µb.permanentURLFiltering.toString(),
hostnameSwitchesString: µb.permanentSwitches.toString(),
Expand Down
31 changes: 16 additions & 15 deletions src/js/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,30 +160,21 @@ const onNetWhitelistReady = function(netWhitelistRaw, adminExtra) {
// User settings are in memory

const onUserSettingsReady = function(fetched) {
const userSettings = µb.userSettings;

// List of external lists is meant to be an array
if ( typeof fetched.externalLists === 'string' ) {
fetched.externalLists =
fetched.externalLists.trim().split(/[\n\r]+/);
}

fromFetch(userSettings, fetched);
fromFetch(µb.userSettings, fetched);

if ( µb.privacySettingsSupported ) {
vAPI.browserSettings.set({
'hyperlinkAuditing': !userSettings.hyperlinkAuditingDisabled,
'prefetching': !userSettings.prefetchingDisabled,
'webrtcIPAddress': !userSettings.webrtcIPAddressHidden
'hyperlinkAuditing': !µb.userSettings.hyperlinkAuditingDisabled,
'prefetching': !µb.userSettings.prefetchingDisabled,
'webrtcIPAddress': !µb.userSettings.webrtcIPAddressHidden
});
}

µb.permanentFirewall.fromString(fetched.dynamicFilteringString);
µb.sessionFirewall.assign(µb.permanentFirewall);
µb.permanentURLFiltering.fromString(fetched.urlFilteringString);
µb.sessionURLFiltering.assign(µb.permanentURLFiltering);
µb.permanentSwitches.fromString(fetched.hostnameSwitchesString);
µb.sessionSwitches.assign(µb.permanentSwitches);
};

/******************************************************************************/
Expand Down Expand Up @@ -219,8 +210,15 @@ const onFirstFetchReady = function(fetched, adminExtra) {

// Order is important -- do not change:
fromFetch(µb.localSettings, fetched);
onUserSettingsReady(fetched);
fromFetch(µb.restoreBackupSettings, fetched);

µb.permanentFirewall.fromString(fetched.dynamicFilteringString);
µb.sessionFirewall.assign(µb.permanentFirewall);
µb.permanentURLFiltering.fromString(fetched.urlFilteringString);
µb.sessionURLFiltering.assign(µb.permanentURLFiltering);
µb.permanentSwitches.fromString(fetched.hostnameSwitchesString);
µb.sessionSwitches.assign(µb.permanentSwitches);

onNetWhitelistReady(fetched.netWhitelist, adminExtra);
onVersionReady(fetched.version);
};
Expand Down Expand Up @@ -269,7 +267,6 @@ const createDefaultProps = function() {
fetchableProps.hostnameSwitchesString += '\nno-csp-reports: * true';
}
toFetch(µb.localSettings, fetchableProps);
toFetch(µb.userSettings, fetchableProps);
toFetch(µb.restoreBackupSettings, fetchableProps);
return fetchableProps;
};
Expand Down Expand Up @@ -321,6 +318,10 @@ try {
log.info(`First fetch ready ${Date.now()-vAPI.T0} ms after launch`);
onFirstFetchReady(fetched, adminExtra);
}),
µb.loadUserSettings().then(fetched => {
log.info(`User settings ready ${Date.now()-vAPI.T0} ms after launch`);
onUserSettingsReady(fetched);
}),
µb.loadPublicSuffixList().then(( ) => {
log.info(`PSL ready ${Date.now()-vAPI.T0} ms after launch`);
}),
Expand Down
71 changes: 52 additions & 19 deletions src/js/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,49 @@

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

µBlock.loadUserSettings = async function() {
const usDefault = this.userSettingsDefault;

const results = await Promise.all([
vAPI.storage.get(Object.assign(usDefault)),
vAPI.adminStorage.get('userSettings'),
]);

const usUser = results[0] instanceof Object && results[0] ||
Object.assign(usDefault);

if ( Array.isArray(results[1]) ) {
const adminSettings = results[1];
for ( const entry of adminSettings ) {
if ( entry.length < 1 ) { continue; }
const name = entry[0];
if ( usDefault.hasOwnProperty(name) === false ) { continue; }
const value = entry.length < 2
? usDefault[name]
: this.settingValueFromString(usDefault, name, entry[1]);
if ( value === undefined ) { continue; }
usUser[name] = usDefault[name] = value;
}
}

return usUser;
};

µBlock.saveUserSettings = function() {
vAPI.storage.set(this.userSettings);
const toSave = this.getModifiedSettings(
this.userSettings,
this.userSettingsDefault
);
const toRemove = [];
for ( const key in this.userSettings ) {
if ( this.userSettings.hasOwnProperty(key) === false ) { continue; }
if ( toSave.hasOwnProperty(key) === false ) { continue; }
toRemove.push(key);
}
if ( toRemove.length !== 0 ) {
vAPI.storage.remove(toRemove);
}
vAPI.storage.set(toSave);
};

/******************************************************************************/
Expand Down Expand Up @@ -164,24 +205,16 @@
};

// Note: Save only the settings which values differ from the default ones.
// This way the new default values in the future will properly apply for those
// which were not modified by the user.

µBlock.getModifiedHiddenSettings = function() {
const out = {};
for ( const prop in this.hiddenSettings ) {
if (
this.hiddenSettings.hasOwnProperty(prop) &&
this.hiddenSettings[prop] !== this.hiddenSettingsDefault[prop]
) {
out[prop] = this.hiddenSettings[prop];
}
}
return out;
};
// This way the new default values in the future will properly apply for
// those which were not modified by the user.

µBlock.saveHiddenSettings = function() {
vAPI.storage.set({ hiddenSettings: this.getModifiedHiddenSettings() });
vAPI.storage.set({
hiddenSettings: this.getModifiedSettings(
this.hiddenSettings,
this.hiddenSettingsDefault
)
});
};

self.addEventListener('hiddenSettingsChanged', ( ) => {
Expand Down Expand Up @@ -404,7 +437,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
const result = Array.from(selectedListKeySet);
if ( externalLists.join() !== this.userSettings.externalLists.join() ) {
this.userSettings.externalLists = externalLists;
vAPI.storage.set({ externalLists });
this.saveUserSettings();
}
this.saveSelectedFilterLists(result);
};
Expand Down Expand Up @@ -599,7 +632,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
this.assets.registerAssetSource(listURL, newEntry);
importedListKeys.push(listURL);
this.userSettings.externalLists.push(listURL.trim());
vAPI.storage.set({ externalLists: this.userSettings.externalLists });
this.saveUserSettings();
this.saveSelectedFilterLists([ listURL ], true);
};

Expand Down
43 changes: 43 additions & 0 deletions src/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -684,3 +684,46 @@
window.dispatchEvent(new CustomEvent(name));
}
};

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

µBlock.getModifiedSettings = function(edit, orig = {}) {
const out = {};
for ( const prop in edit ) {
if ( orig.hasOwnProperty(prop) && edit[prop] !== orig[prop] ) {
out[prop] = edit[prop];
}
}
return out;
};

µBlock.settingValueFromString = function(orig, name, s) {
if ( typeof name !== 'string' || typeof s !== 'string' ) { return; }
if ( orig.hasOwnProperty(name) === false ) { return; }
let r;
switch ( typeof orig[name] ) {
case 'boolean':
if ( s === 'true' ) {
r = true;
} else if ( s === 'false' ) {
r = false;
}
break;
case 'string':
r = s.trim();
break;
case 'number':
if ( s.startsWith('0b') ) {
r = parseInt(s.slice(2), 2);
} else if ( s.startsWith('0x') ) {
r = parseInt(s.slice(2), 16);
} else {
r = parseInt(s, 10);
}
if ( isNaN(r) ) { r = undefined; }
break;
default:
break;
}
return r;
};

0 comments on commit 6eb1246

Please sign in to comment.