Work toward modernizing code base: promisification

Swathes of code have been converted to use
Promises/async/await. More left to do.

Related commits:
- eec53c0154
- 915687fddb
- 55cc0c6997
- e27328f931
This commit is contained in:
Raymond Hill 2019-09-16 16:17:48 -04:00
parent c74df9b172
commit 0051f3b5c7
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
10 changed files with 171 additions and 175 deletions

View File

@ -58,10 +58,6 @@ window.addEventListener('webextFlavor', function() {
vAPI.webextFlavor.soup.has('user_stylesheet');
}, { once: true });
vAPI.insertCSS = function(tabId, details) {
return chrome.tabs.insertCSS(tabId, details, vAPI.resetLastError);
};
const noopFunc = function(){};
/******************************************************************************/
@ -364,6 +360,16 @@ vAPI.Tabs = class {
});
}
async executeScript() {
let result;
try {
result = await webext.tabs.executeScript(...arguments);
}
catch(reason) {
}
return Array.isArray(result) ? result : [];
}
async get(tabId) {
if ( tabId === null ) {
return this.getCurrent();
@ -383,6 +389,14 @@ vAPI.Tabs = class {
return tabs.length !== 0 ? tabs[0] : null;
}
async insertCSS() {
try {
await webext.tabs.insertCSS(...arguments);
}
catch(reason) {
}
}
async query(queryInfo) {
let tabs;
try {
@ -393,6 +407,14 @@ vAPI.Tabs = class {
return Array.isArray(tabs) ? tabs : [];
}
async removeCSS() {
try {
await webext.tabs.removeCSS(...arguments);
}
catch(reason) {
}
}
// Properties of the details object:
// - url: 'URL', => the address that will be opened
// - index: -1, => undefined: end of the list, -1: following tab,
@ -600,28 +622,6 @@ vAPI.Tabs = class {
vAPI.windows.update(tab.windowId, { focused: true });
}
injectScript(tabId, details, callback) {
const onScriptExecuted = function() {
// https://code.google.com/p/chromium/issues/detail?id=410868#c8
void browser.runtime.lastError;
if ( typeof callback === 'function' ) {
callback.apply(null, arguments);
}
};
if ( tabId ) {
browser.tabs.executeScript(
toTabId(tabId),
details,
onScriptExecuted
);
} else {
browser.tabs.executeScript(
details,
onScriptExecuted
);
}
}
// https://forums.lanik.us/viewtopic.php?f=62&t=32826
// Chromium-based browsers: sanitize target URL. I've seen data: URI with
// newline characters in standard fields, possibly as a way of evading
@ -984,29 +984,20 @@ vAPI.messaging = {
if ( msg.add ) {
details.runAt = 'document_start';
}
let countdown = 0;
const countdownHandler = function() {
void chrome.runtime.lastError;
countdown -= 1;
if ( countdown === 0 && typeof callback === 'function' ) {
callback();
}
};
const promises = [];
for ( const cssText of msg.add ) {
countdown += 1;
details.code = cssText;
browser.tabs.insertCSS(tabId, details, countdownHandler);
promises.push(vAPI.tabs.insertCSS(tabId, details));
}
if ( typeof chrome.tabs.removeCSS === 'function' ) {
if ( typeof webext.tabs.removeCSS === 'function' ) {
for ( const cssText of msg.remove ) {
countdown += 1;
details.code = cssText;
browser.tabs.removeCSS(tabId, details, countdownHandler);
promises.push(vAPI.tabs.removeCSS(tabId, details));
}
}
if ( countdown === 0 && typeof callback === 'function' ) {
Promise.all(promises).then(( ) => {
callback();
}
});
break;
}
},

View File

@ -94,6 +94,22 @@ const webext = { // jshint ignore:line
});
});
},
executeScript: function() {
return new Promise(resolve => {
chrome.tabs.executeScript(...arguments, result => {
void chrome.runtime.lastError;
resolve(result);
});
});
},
insertCSS: function() {
return new Promise(resolve => {
chrome.tabs.insertCSS(...arguments, ( ) => {
void chrome.runtime.lastError;
resolve();
});
});
},
query: function() {
return new Promise(resolve => {
chrome.tabs.query(...arguments, tabs => {
@ -113,7 +129,7 @@ const webext = { // jshint ignore:line
},
windows: {
get: async function() {
get: function() {
return new Promise(resolve => {
chrome.windows.get(...arguments, win => {
void chrome.runtime.lastError;
@ -121,7 +137,7 @@ const webext = { // jshint ignore:line
});
});
},
create: async function() {
create: function() {
return new Promise(resolve => {
chrome.windows.create(...arguments, win => {
void chrome.runtime.lastError;
@ -129,7 +145,7 @@ const webext = { // jshint ignore:line
});
});
},
update: async function() {
update: function() {
return new Promise(resolve => {
chrome.windows.update(...arguments, win => {
void chrome.runtime.lastError;
@ -140,6 +156,18 @@ const webext = { // jshint ignore:line
},
};
// https://bugs.chromium.org/p/chromium/issues/detail?id=608854
if ( chrome.tabs.removeCSS instanceof Function ) {
webext.tabs.removeCSS = function() {
return new Promise(resolve => {
chrome.tabs.removeCSS(...arguments, ( ) => {
void chrome.runtime.lastError;
resolve();
});
});
};
}
if ( chrome.storage.managed instanceof Object ) {
webext.storage.managed = {
get: function() {

View File

@ -65,8 +65,6 @@ const µBlock = (function() { // jshint ignore:line
};
return {
firstInstall: false,
userSettings: {
advancedUserEnabled: false,
alwaysDetachLogger: true,

View File

@ -922,7 +922,7 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) {
out.complex = [];
}
out.injected = injected.join(',\n');
vAPI.insertCSS(request.tabId, {
vAPI.tabs.insertCSS(request.tabId, {
code: out.injected + '\n{display:none!important;}',
cssOrigin: 'user',
frameId: request.frameId,
@ -1105,11 +1105,11 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
};
if ( out.injectedHideFilters.length !== 0 ) {
details.code = out.injectedHideFilters + '\n{display:none!important;}';
vAPI.insertCSS(request.tabId, details);
vAPI.tabs.insertCSS(request.tabId, details);
}
if ( out.networkFilters.length !== 0 ) {
details.code = out.networkFilters + '\n{display:none!important;}';
vAPI.insertCSS(request.tabId, details);
vAPI.tabs.insertCSS(request.tabId, details);
out.networkFilters = '';
}
}

View File

@ -386,7 +386,7 @@ const onMessage = function(request, sender, callback) {
if ( pageStore !== null ) {
pageStore.hiddenElementCount = 0;
pageStore.scriptCount = 0;
vAPI.tabs.injectScript(request.tabId, {
vAPI.tabs.executeScript(request.tabId, {
allFrames: true,
file: '/js/scriptlets/dom-survey.js',
runAt: 'document_end'
@ -539,14 +539,11 @@ const onMessage = function(request, sender, callback) {
if ( pageStore === null ) { break; }
const fctxt = µb.filteringContext.fromTabId(tabId);
if ( pageStore.filterScripting(fctxt, undefined) ) {
vAPI.tabs.injectScript(
tabId,
{
file: '/js/scriptlets/noscript-spoof.js',
frameId: frameId,
runAt: 'document_end'
}
);
vAPI.tabs.executeScript(tabId, {
file: '/js/scriptlets/noscript-spoof.js',
frameId: frameId,
runAt: 'document_end'
});
}
break;

View File

@ -394,7 +394,7 @@ const PageStore = class {
}
injectLargeMediaElementScriptlet() {
vAPI.tabs.injectScript(this.tabId, {
vAPI.tabs.executeScript(this.tabId, {
file: '/js/scriptlets/load-large-media-interactive.js',
allFrames: true,
runAt: 'document_idle',

View File

@ -449,15 +449,12 @@
if ( µb.hiddenSettings.debugScriptletInjector ) {
code = 'debugger;\n' + code;
}
vAPI.tabs.injectScript(
details.tabId,
{
code: code,
frameId: details.frameId,
matchAboutBlank: false,
runAt: 'document_start'
}
);
vAPI.tabs.executeScript(details.tabId, {
code: code,
frameId: details.frameId,
matchAboutBlank: false,
runAt: 'document_start'
});
};
api.toSelfie = function() {

View File

@ -48,87 +48,20 @@ vAPI.app.onShutdown = function() {
/******************************************************************************/
// Final initialization steps after all needed assets are in memory.
// - Initialize internal state with maybe already existing tabs.
// - Schedule next update operation.
const onAllReady = function() {
µb.webRequest.start();
// Ensure that the resources allocated for decompression purpose (likely
// large buffers) are garbage-collectable immediately after launch.
// Otherwise I have observed that it may take quite a while before the
// garbage collection of these resources kicks in. Relinquishing as soon
// as possible ensure minimal memory usage baseline.
µb.lz4Codec.relinquish();
initializeTabs();
// https://github.com/chrisaljoudi/uBlock/issues/184
// Check for updates not too far in the future.
µb.assets.addObserver(µb.assetObserver.bind(µb));
µb.scheduleAssetUpdater(
µb.userSettings.autoUpdate
? µb.hiddenSettings.autoUpdateDelayAfterLaunch * 1000
: 0
);
// vAPI.cloud is optional.
if ( µb.cloudStorageSupported ) {
vAPI.cloud.start([
'tpFiltersPane',
'myFiltersPane',
'myRulesPane',
'whitelistPane'
]);
}
µb.contextMenu.update(null);
µb.firstInstall = false;
// https://github.com/uBlockOrigin/uBlock-issues/issues/717
// Prevent the extensions from being restarted mid-session.
browser.runtime.onUpdateAvailable.addListener(details => {
const toInt = vAPI.app.intFromVersion;
if (
µBlock.hiddenSettings.extensionUpdateForceReload === true ||
toInt(details.version) <= toInt(vAPI.app.version)
) {
vAPI.app.restart();
}
});
};
/******************************************************************************/
// This is called only once, when everything has been loaded in memory after
// the extension was launched. It can be used to inject content scripts
// in already opened web pages, to remove whatever nuisance could make it to
// the web pages before uBlock was ready.
const initializeTabs = async function() {
const handleScriptResponse = function(tabId, results) {
if (
Array.isArray(results) === false ||
results.length === 0 ||
results[0] !== true
) {
return;
}
// Inject dclarative content scripts programmatically.
const manifest = chrome.runtime.getManifest();
if ( manifest instanceof Object === false ) { return; }
for ( const contentScript of manifest.content_scripts ) {
for ( const file of contentScript.js ) {
vAPI.tabs.injectScript(tabId, {
file: file,
allFrames: contentScript.all_frames,
runAt: contentScript.run_at
});
}
}
};
const manifest = browser.runtime.getManifest();
if ( manifest instanceof Object === false ) { return; }
const tabs = await vAPI.tabs.query({ url: '<all_urls>' });
const toCheck = [];
const checker = {
file: 'js/scriptlets/should-inject-contentscript.js'
};
for ( const tab of tabs ) {
µb.tabContextManager.commit(tab.id, tab.url);
µb.bindTabToPageStats(tab.id);
@ -136,13 +69,28 @@ const initializeTabs = async function() {
// Find out whether content scripts need to be injected
// programmatically. This may be necessary for web pages which
// were loaded before uBO launched.
if ( /^https?:\/\//.test(tab.url) === false ) { continue; }
vAPI.tabs.injectScript(
tab.id,
{ file: 'js/scriptlets/should-inject-contentscript.js' },
handleScriptResponse.bind(null, tab.id)
toCheck.push(
/^https?:\/\//.test(tab.url)
? vAPI.tabs.executeScript(tab.id, checker)
: false
);
}
const results = await Promise.all(toCheck);
for ( let i = 0; i < results.length; i++ ) {
const result = results[i];
if ( result.length === 0 || result[0] !== true ) { continue; }
// Inject dclarative content scripts programmatically.
const tabId = tabs[i].id;
for ( const contentScript of manifest.content_scripts ) {
for ( const file of contentScript.js ) {
vAPI.tabs.executeScript(tabId, {
file: file,
allFrames: contentScript.all_frames,
runAt: contentScript.run_at
});
}
}
}
};
/******************************************************************************/
@ -250,9 +198,6 @@ const onFirstFetchReady = function(fetched) {
fetched = createDefaultProps();
}
// https://github.com/gorhill/uBlock/issues/747
µb.firstInstall = fetched.version === '0.0.0.0';
// Order is important -- do not change:
onSystemSettingsReady(fetched);
fromFetch(µb.localSettings, fetched);
@ -350,7 +295,53 @@ try {
console.trace(ex);
}
onAllReady();
// Final initialization steps after all needed assets are in memory.
µb.webRequest.start();
// Ensure that the resources allocated for decompression purpose (likely
// large buffers) are garbage-collectable immediately after launch.
// Otherwise I have observed that it may take quite a while before the
// garbage collection of these resources kicks in. Relinquishing as soon
// as possible ensure minimal memory usage baseline.
µb.lz4Codec.relinquish();
// Initialize internal state with maybe already existing tabs.
initializeTabs();
// https://github.com/chrisaljoudi/uBlock/issues/184
// Check for updates not too far in the future.
µb.assets.addObserver(µb.assetObserver.bind(µb));
µb.scheduleAssetUpdater(
µb.userSettings.autoUpdate
? µb.hiddenSettings.autoUpdateDelayAfterLaunch * 1000
: 0
);
// vAPI.cloud is optional.
if ( µb.cloudStorageSupported ) {
vAPI.cloud.start([
'tpFiltersPane',
'myFiltersPane',
'myRulesPane',
'whitelistPane'
]);
}
µb.contextMenu.update(null);
// https://github.com/uBlockOrigin/uBlock-issues/issues/717
// Prevent the extensions from being restarted mid-session.
browser.runtime.onUpdateAvailable.addListener(details => {
const toInt = vAPI.app.intFromVersion;
if (
µBlock.hiddenSettings.extensionUpdateForceReload === true ||
toInt(details.version) <= toInt(vAPI.app.version)
) {
vAPI.app.restart();
}
});
log.info(`All ready ${Date.now()-vAPI.T0} ms after launch`);
// <<<<< end of private scope

View File

@ -255,7 +255,8 @@
return;
}
// Select default filter lists if first-time launch.
// https://github.com/gorhill/uBlock/issues/747
// Select default filter lists if first-time launch.
const lists = await this.assets.metadata();
this.saveSelectedFilterLists(this.autoSelectRegionalFilterLists(lists));
};

View File

@ -414,7 +414,7 @@ const matchBucket = function(url, hostname, bucket, start) {
/******************************************************************************/
µBlock.elementPickerExec = function(tabId, targetElement, zap) {
µBlock.elementPickerExec = async function(tabId, targetElement, zap) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
this.epickerTarget = targetElement || '';
@ -422,27 +422,20 @@ const matchBucket = function(url, hostname, bucket, start) {
// https://github.com/uBlockOrigin/uBlock-issues/issues/40
// The element picker needs this library
vAPI.tabs.injectScript(
tabId,
{
file: '/lib/diff/swatinem_diff.js',
runAt: 'document_end'
}
);
vAPI.tabs.executeScript(tabId, {
file: '/lib/diff/swatinem_diff.js',
runAt: 'document_end'
});
await vAPI.tabs.executeScript(tabId, {
file: '/js/scriptlets/element-picker.js',
runAt: 'document_end'
});
// https://github.com/uBlockOrigin/uBlock-issues/issues/168
// Force activate the target tab once the element picker has been
// injected.
vAPI.tabs.injectScript(
tabId,
{
file: '/js/scriptlets/element-picker.js',
runAt: 'document_end'
},
( ) => {
vAPI.tabs.select(tabId);
}
);
vAPI.tabs.select(tabId);
};
/******************************************************************************/
@ -638,7 +631,7 @@ const matchBucket = function(url, hostname, bucket, start) {
// cosmetic filters.
µBlock.logCosmeticFilters = function(tabId, frameId) {
vAPI.tabs.injectScript(tabId, {
vAPI.tabs.executeScript(tabId, {
file: '/js/scriptlets/cosmetic-logger.js',
frameId: frameId,
runAt: 'document_start'
@ -694,15 +687,15 @@ const matchBucket = function(url, hostname, bucket, start) {
}
pendingEntries.set(key, new Entry(tabId, scriptlet, callback));
}
vAPI.tabs.injectScript(tabId, {
file: '/js/scriptlets/' + scriptlet + '.js'
vAPI.tabs.executeScript(tabId, {
file: `/js/scriptlets/${scriptlet}.js`
});
};
// TODO: think about a callback mechanism.
const injectDeep = function(tabId, scriptlet) {
vAPI.tabs.injectScript(tabId, {
file: '/js/scriptlets/' + scriptlet + '.js',
vAPI.tabs.executeScript(tabId, {
file: `/js/scriptlets/${scriptlet}.js`,
allFrames: true
});
};