Add setting to control suspension on network activity at launch

Related discussion:
- a0a9497b4a (commitcomment-62560291)

The new setting, when disabled (enabled by default), allows a user
to prevent uBO from waiting for all filter lists to be loaded
before allowing network activity at launch. The setting is enabled
by default, meaning uBO waits for all filter lists to be loaded in
memory before unsuspending network activity. Some users may find
this behavior undesirable, hence the new setting.

This gives the option to potentially speed up page load at launch,
at the cost of potentially not properly filtering network requests
as per filter lists/rules.

For platforms not supporting the suspension of network activity,
the setting will merely prevent whatever mechanism exists on the
platform to mitigate improper filtering of network requests at
launch. For example, in Chromium-based browsers, unchecking the
new setting will prevent the browser from re-loading tabs for
which there was network activity while in "suspended" state at
launch.
This commit is contained in:
Raymond Hill 2021-12-30 09:24:38 -05:00
parent 0c03244759
commit 925c8d5d0c
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
10 changed files with 52 additions and 54 deletions

View File

@ -186,9 +186,11 @@ vAPI.Tabs = class extends vAPI.Tabs {
return { cancel: true };
}
unsuspendAllRequests() {
for ( const tabId of this.suspendedTabIds ) {
vAPI.tabs.reload(tabId);
unsuspendAllRequests(discard = false) {
if ( discard !== true ) {
for ( const tabId of this.suspendedTabIds ) {
vAPI.tabs.reload(tabId);
}
}
this.suspendedTabIds.clear();
}

View File

@ -1220,12 +1220,10 @@ vAPI.Net = class {
}
unsuspendAllRequests() {
}
suspend(force = false) {
if ( this.canSuspend() || force ) {
this.suspendDepth += 1;
}
suspend() {
this.suspendDepth += 1;
}
unsuspend(all = false) {
unsuspend({ all = false, discard = false } = {}) {
if ( this.suspendDepth === 0 ) { return; }
if ( all ) {
this.suspendDepth = 0;
@ -1233,7 +1231,7 @@ vAPI.Net = class {
this.suspendDepth -= 1;
}
if ( this.suspendDepth !== 0 ) { return; }
this.unsuspendAllRequests();
this.unsuspendAllRequests(discard);
}
canSuspend() {
return false;

View File

@ -292,11 +292,15 @@ import {
this.pendingRequests.push(pending);
return pending.promise;
}
unsuspendAllRequests() {
unsuspendAllRequests(discard = false) {
const pendingRequests = this.pendingRequests;
this.pendingRequests = [];
for ( const entry of pendingRequests ) {
entry.resolve(this.onBeforeSuspendableRequest(entry.details));
entry.resolve(
discard !== true
? this.onBeforeSuspendableRequest(entry.details)
: undefined
);
}
}
canSuspend() {

View File

@ -24,10 +24,17 @@
</div>
<div>
<div class="li"><label><span class="input checkbox"><input type="checkbox" id="autoUpdate"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="3pAutoUpdatePrompt1"></span></label></div>
<div class="li"><label><span class="input checkbox"><input type="checkbox" id="parseCosmeticFilters"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span data-i18n="3pParseAllABPHideFiltersPrompt1"></span>&nbsp;<span class="fa-icon info" data-i18n-title="3pParseAllABPHideFiltersInfo">question-circle</span></span></label>
<div class="li">
<label><span class="input checkbox"><input type="checkbox" id="autoUpdate"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="3pAutoUpdatePrompt1"></span></label>
</div>
<div class="li"><label><span class="input checkbox"><input type="checkbox" id="ignoreGenericCosmeticFilters"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span data-i18n="3pIgnoreGenericCosmeticFilters"></span>&nbsp;<span class="fa-icon info" data-i18n-title="3pIgnoreGenericCosmeticFiltersInfo">question-circle</span></span></label>
<div class="li">
<label><span class="input checkbox"><input type="checkbox" id="parseCosmeticFilters"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span data-i18n="3pParseAllABPHideFiltersPrompt1"></span>&nbsp;<span class="fa-icon info" data-i18n-title="3pParseAllABPHideFiltersInfo">question-circle</span></span></label>
</div>
<div class="li">
<label><span class="input checkbox"><input type="checkbox" id="ignoreGenericCosmeticFilters"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span data-i18n="3pIgnoreGenericCosmeticFilters"></span>&nbsp;<span class="fa-icon info" data-i18n-title="3pIgnoreGenericCosmeticFiltersInfo">question-circle</span></span></label>
</div>
<div class="li">
<label><span class="input checkbox"><input type="checkbox" id="suspendUntilListsAreLoaded"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="3pSuspendUntilListsAreLoaded"></span></label>
</div>
</div>

View File

@ -286,7 +286,8 @@ const renderFilterLists = function(soft) {
// Re-insert import widget.
uDom('[data-groupkey="custom"] .listEntries').append(importWidget);
uDom.nodeFromId('autoUpdate').checked = listDetails.autoUpdate === true;
uDom.nodeFromId('autoUpdate').checked =
listDetails.autoUpdate === true;
uDom.nodeFromId('listsOfBlockedHostsPrompt').textContent =
vAPI.i18n('3pListsOfBlockedHostsPrompt')
.replace(
@ -301,6 +302,8 @@ const renderFilterLists = function(soft) {
listDetails.parseCosmeticFilters === true;
uDom.nodeFromId('ignoreGenericCosmeticFilters').checked =
listDetails.ignoreGenericCosmeticFilters === true;
uDom.nodeFromId('suspendUntilListsAreLoaded').checked =
listDetails.suspendUntilListsAreLoaded === true;
// Compute a hash of the settings so that we can keep track of changes
// affecting the loading of filter lists.
@ -529,11 +532,12 @@ const buttonPurgeAllHandler = async function(hard) {
/******************************************************************************/
const autoUpdateCheckboxChanged = function() {
const userSettingCheckboxChanged = function() {
const target = event.target;
messaging.send('dashboard', {
what: 'userSettings',
name: 'autoUpdate',
value: this.checked,
name: target.id,
value: target.checked,
});
};
@ -683,9 +687,10 @@ self.hasUnsavedData = function() {
/******************************************************************************/
uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
uDom('#autoUpdate').on('change', userSettingCheckboxChanged);
uDom('#parseCosmeticFilters').on('change', onFilteringSettingsChanged);
uDom('#ignoreGenericCosmeticFilters').on('change', onFilteringSettingsChanged);
uDom('#suspendUntilListsAreLoaded').on('change', userSettingCheckboxChanged);
uDom('#buttonApply').on('click', ( ) => { buttonApplyHandler(); });
uDom('#buttonUpdate').on('click', ( ) => { buttonUpdateHandler(); });
uDom('#buttonPurgeAll').on('click', ev => {

View File

@ -80,7 +80,6 @@ const hiddenSettingsDefault = {
requestJournalProcessPeriod: 1000,
selfieAfter: 2,
strictBlockingBypassDuration: 120,
suspendTabsUntilReady: 'unset',
uiPopupConfig: 'unset',
uiFlavor: 'unset',
uiStyles: 'unset',
@ -109,6 +108,7 @@ const userSettingsDefault = {
prefetchingDisabled: true,
requestLogMaxEntries: 1000,
showIconBadge: true,
suspendUntilListsAreLoaded: true,
tooltipsDisabled: false,
webrtcIPAddressHidden: false,
};
@ -214,7 +214,7 @@ const µBlock = { // jshint ignore:line
readyToFilter: false,
supportStats: {
launchToReadiness: '',
allReadyAfter: '',
},
pageStores: new Map(),

View File

@ -1091,6 +1091,7 @@ const getLists = async function(callback) {
isUpdating: io.isUpdating(),
netFilterCount: staticNetFilteringEngine.getFilterCount(),
parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters,
suspendUntilListsAreLoaded: µb.userSettings.suspendUntilListsAreLoaded,
userFiltersPath: µb.userFiltersPath
};
const [ lists, metadata ] = await Promise.all([

View File

@ -192,6 +192,13 @@ const onNetWhitelistReady = function(netWhitelistRaw, adminExtra) {
// User settings are in memory
const onUserSettingsReady = function(fetched) {
// Terminate suspended state?
if ( fetched.suspendUntilListsAreLoaded === false ) {
vAPI.net.unsuspend({ all: true, discard: true });
ubolog(`Unsuspend network activity listener`);
µb.supportStats.unsuspendAfter = `${Date.now() - vAPI.T0} ms`;
}
// `externalLists` will be deprecated in some future, it is kept around
// for forward compatibility purpose, and should reflect the content of
// `importedLists`.
@ -282,13 +289,6 @@ const onHiddenSettingsReady = async function() {
ubolog(`Override default webext flavor with ${tokens}`);
}
// Maybe override current network listener suspend state
if ( µb.hiddenSettings.suspendTabsUntilReady === 'no' ) {
vAPI.net.unsuspend(true);
} else if ( µb.hiddenSettings.suspendTabsUntilReady === 'yes' ) {
vAPI.net.suspend(true);
}
// Maybe disable WebAssembly
if ( vAPI.canWASM && µb.hiddenSettings.disableWebAssembly !== true ) {
const wasmModuleFetcher = function(path) {
@ -506,11 +506,11 @@ browser.runtime.onUpdateAvailable.addListener(details => {
}
});
µb.supportStats.launchToReadiness = `${Date.now() - vAPI.T0} ms`;
µb.supportStats.allReadyAfter = `${Date.now() - vAPI.T0} ms`;
if ( selfieIsValid ) {
µb.supportStats.launchToReadiness += ' (selfie)';
µb.supportStats.allReadyAfter += ' (selfie)';
}
ubolog(`All ready ${µb.supportStats.launchToReadiness} ms after launch`);
ubolog(`All ready ${µb.supportStats.allReadyAfter} ms after launch`);
// <<<<< end of private scope
})();

View File

@ -239,12 +239,6 @@ import {
if ( typeof hs[key] !== typeof hsDefault[key] ) { continue; }
this.hiddenSettings[key] = hs[key];
}
if ( typeof this.hiddenSettings.suspendTabsUntilReady === 'boolean' ) {
this.hiddenSettings.suspendTabsUntilReady =
this.hiddenSettings.suspendTabsUntilReady
? 'yes'
: 'unset';
}
this.fireDOMEvent('hiddenSettingsChanged');
};
@ -810,7 +804,9 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
const onFilterListsReady = function(lists) {
this.availableFilterLists = lists;
vAPI.net.suspend();
if ( vAPI.net.canSuspend() ) {
vAPI.net.suspend();
}
redirectEngine.reset();
staticExtFilteringEngine.reset();
staticNetFilteringEngine.reset();

View File

@ -1146,22 +1146,7 @@ const webRequest = {
{ urls: [ 'http://*/*', 'https://*/*' ] },
[ 'blocking', 'responseHeaders' ]
);
vAPI.net.unsuspend(true);
// Mitigation: force-reload active tabs for environments not
// supporting suspended network request listeners.
if (
vAPI.net.canSuspend() !== true ||
µb.hiddenSettings.suspendTabsUntilReady === 'no'
) {
const tabs = await vAPI.tabs.query({
active: true,
url: [ 'https://*/*', 'http://*/*' ],
windowType: 'normal',
});
for ( const tab of tabs ) {
vAPI.tabs.reload(tab.id);
}
}
vAPI.net.unsuspend({ all: true });
};
})(),