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'); vAPI.webextFlavor.soup.has('user_stylesheet');
}, { once: true }); }, { once: true });
vAPI.insertCSS = function(tabId, details) {
return chrome.tabs.insertCSS(tabId, details, vAPI.resetLastError);
};
const noopFunc = function(){}; 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) { async get(tabId) {
if ( tabId === null ) { if ( tabId === null ) {
return this.getCurrent(); return this.getCurrent();
@ -383,6 +389,14 @@ vAPI.Tabs = class {
return tabs.length !== 0 ? tabs[0] : null; return tabs.length !== 0 ? tabs[0] : null;
} }
async insertCSS() {
try {
await webext.tabs.insertCSS(...arguments);
}
catch(reason) {
}
}
async query(queryInfo) { async query(queryInfo) {
let tabs; let tabs;
try { try {
@ -393,6 +407,14 @@ vAPI.Tabs = class {
return Array.isArray(tabs) ? tabs : []; return Array.isArray(tabs) ? tabs : [];
} }
async removeCSS() {
try {
await webext.tabs.removeCSS(...arguments);
}
catch(reason) {
}
}
// Properties of the details object: // Properties of the details object:
// - url: 'URL', => the address that will be opened // - url: 'URL', => the address that will be opened
// - index: -1, => undefined: end of the list, -1: following tab, // - index: -1, => undefined: end of the list, -1: following tab,
@ -600,28 +622,6 @@ vAPI.Tabs = class {
vAPI.windows.update(tab.windowId, { focused: true }); 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 // https://forums.lanik.us/viewtopic.php?f=62&t=32826
// Chromium-based browsers: sanitize target URL. I've seen data: URI with // Chromium-based browsers: sanitize target URL. I've seen data: URI with
// newline characters in standard fields, possibly as a way of evading // newline characters in standard fields, possibly as a way of evading
@ -984,29 +984,20 @@ vAPI.messaging = {
if ( msg.add ) { if ( msg.add ) {
details.runAt = 'document_start'; details.runAt = 'document_start';
} }
let countdown = 0; const promises = [];
const countdownHandler = function() {
void chrome.runtime.lastError;
countdown -= 1;
if ( countdown === 0 && typeof callback === 'function' ) {
callback();
}
};
for ( const cssText of msg.add ) { for ( const cssText of msg.add ) {
countdown += 1;
details.code = cssText; 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 ) { for ( const cssText of msg.remove ) {
countdown += 1;
details.code = cssText; 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(); callback();
} });
break; 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() { query: function() {
return new Promise(resolve => { return new Promise(resolve => {
chrome.tabs.query(...arguments, tabs => { chrome.tabs.query(...arguments, tabs => {
@ -113,7 +129,7 @@ const webext = { // jshint ignore:line
}, },
windows: { windows: {
get: async function() { get: function() {
return new Promise(resolve => { return new Promise(resolve => {
chrome.windows.get(...arguments, win => { chrome.windows.get(...arguments, win => {
void chrome.runtime.lastError; void chrome.runtime.lastError;
@ -121,7 +137,7 @@ const webext = { // jshint ignore:line
}); });
}); });
}, },
create: async function() { create: function() {
return new Promise(resolve => { return new Promise(resolve => {
chrome.windows.create(...arguments, win => { chrome.windows.create(...arguments, win => {
void chrome.runtime.lastError; void chrome.runtime.lastError;
@ -129,7 +145,7 @@ const webext = { // jshint ignore:line
}); });
}); });
}, },
update: async function() { update: function() {
return new Promise(resolve => { return new Promise(resolve => {
chrome.windows.update(...arguments, win => { chrome.windows.update(...arguments, win => {
void chrome.runtime.lastError; 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 ) { if ( chrome.storage.managed instanceof Object ) {
webext.storage.managed = { webext.storage.managed = {
get: function() { get: function() {

View File

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

View File

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

View File

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

View File

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

View File

@ -449,15 +449,12 @@
if ( µb.hiddenSettings.debugScriptletInjector ) { if ( µb.hiddenSettings.debugScriptletInjector ) {
code = 'debugger;\n' + code; code = 'debugger;\n' + code;
} }
vAPI.tabs.injectScript( vAPI.tabs.executeScript(details.tabId, {
details.tabId,
{
code: code, code: code,
frameId: details.frameId, frameId: details.frameId,
matchAboutBlank: false, matchAboutBlank: false,
runAt: 'document_start' runAt: 'document_start'
} });
);
}; };
api.toSelfie = function() { 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 // 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 // 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 // in already opened web pages, to remove whatever nuisance could make it to
// the web pages before uBlock was ready. // the web pages before uBlock was ready.
const initializeTabs = async function() { const initializeTabs = async function() {
const handleScriptResponse = function(tabId, results) { const manifest = browser.runtime.getManifest();
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; } 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 tabs = await vAPI.tabs.query({ url: '<all_urls>' }); 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 ) { for ( const tab of tabs ) {
µb.tabContextManager.commit(tab.id, tab.url); µb.tabContextManager.commit(tab.id, tab.url);
µb.bindTabToPageStats(tab.id); µb.bindTabToPageStats(tab.id);
@ -136,13 +69,28 @@ const initializeTabs = async function() {
// Find out whether content scripts need to be injected // Find out whether content scripts need to be injected
// programmatically. This may be necessary for web pages which // programmatically. This may be necessary for web pages which
// were loaded before uBO launched. // were loaded before uBO launched.
if ( /^https?:\/\//.test(tab.url) === false ) { continue; } toCheck.push(
vAPI.tabs.injectScript( /^https?:\/\//.test(tab.url)
tab.id, ? vAPI.tabs.executeScript(tab.id, checker)
{ file: 'js/scriptlets/should-inject-contentscript.js' }, : false
handleScriptResponse.bind(null, tab.id)
); );
} }
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(); fetched = createDefaultProps();
} }
// https://github.com/gorhill/uBlock/issues/747
µb.firstInstall = fetched.version === '0.0.0.0';
// Order is important -- do not change: // Order is important -- do not change:
onSystemSettingsReady(fetched); onSystemSettingsReady(fetched);
fromFetch(µb.localSettings, fetched); fromFetch(µb.localSettings, fetched);
@ -350,7 +295,53 @@ try {
console.trace(ex); 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`); log.info(`All ready ${Date.now()-vAPI.T0} ms after launch`);
// <<<<< end of private scope // <<<<< end of private scope

View File

@ -255,6 +255,7 @@
return; return;
} }
// https://github.com/gorhill/uBlock/issues/747
// Select default filter lists if first-time launch. // Select default filter lists if first-time launch.
const lists = await this.assets.metadata(); const lists = await this.assets.metadata();
this.saveSelectedFilterLists(this.autoSelectRegionalFilterLists(lists)); 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; } if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
this.epickerTarget = targetElement || ''; this.epickerTarget = targetElement || '';
@ -422,27 +422,20 @@ const matchBucket = function(url, hostname, bucket, start) {
// https://github.com/uBlockOrigin/uBlock-issues/issues/40 // https://github.com/uBlockOrigin/uBlock-issues/issues/40
// The element picker needs this library // The element picker needs this library
vAPI.tabs.injectScript( vAPI.tabs.executeScript(tabId, {
tabId,
{
file: '/lib/diff/swatinem_diff.js', file: '/lib/diff/swatinem_diff.js',
runAt: 'document_end' 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 // https://github.com/uBlockOrigin/uBlock-issues/issues/168
// Force activate the target tab once the element picker has been // Force activate the target tab once the element picker has been
// injected. // 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. // cosmetic filters.
µBlock.logCosmeticFilters = function(tabId, frameId) { µBlock.logCosmeticFilters = function(tabId, frameId) {
vAPI.tabs.injectScript(tabId, { vAPI.tabs.executeScript(tabId, {
file: '/js/scriptlets/cosmetic-logger.js', file: '/js/scriptlets/cosmetic-logger.js',
frameId: frameId, frameId: frameId,
runAt: 'document_start' runAt: 'document_start'
@ -694,15 +687,15 @@ const matchBucket = function(url, hostname, bucket, start) {
} }
pendingEntries.set(key, new Entry(tabId, scriptlet, callback)); pendingEntries.set(key, new Entry(tabId, scriptlet, callback));
} }
vAPI.tabs.injectScript(tabId, { vAPI.tabs.executeScript(tabId, {
file: '/js/scriptlets/' + scriptlet + '.js' file: `/js/scriptlets/${scriptlet}.js`
}); });
}; };
// TODO: think about a callback mechanism. // TODO: think about a callback mechanism.
const injectDeep = function(tabId, scriptlet) { const injectDeep = function(tabId, scriptlet) {
vAPI.tabs.injectScript(tabId, { vAPI.tabs.executeScript(tabId, {
file: '/js/scriptlets/' + scriptlet + '.js', file: `/js/scriptlets/${scriptlet}.js`,
allFrames: true allFrames: true
}); });
}; };